JavaEE实战——XML文档DOM、SAX、STAX解析方式详解

2016年06月22日 23:10:35
李春春_
阅读数:3445

													<span class="tags-box artic-tag-box">
<span class="label">标签:</span>
<a data-track-click="{&quot;mod&quot;:&quot;popu_626&quot;,&quot;con&quot;:&quot;DOM&quot;}" class="tag-link" href="http://so.csdn.net/so/search/s.do?q=DOM&amp;t=blog" target="_blank">DOM </a><a data-track-click="{&quot;mod&quot;:&quot;popu_626&quot;,&quot;con&quot;:&quot;SAX&quot;}" class="tag-link" href="http://so.csdn.net/so/search/s.do?q=SAX&amp;t=blog" target="_blank">SAX </a><a data-track-click="{&quot;mod&quot;:&quot;popu_626&quot;,&quot;con&quot;:&quot;STAX&quot;}" class="tag-link" href="http://so.csdn.net/so/search/s.do?q=STAX&amp;t=blog" target="_blank">STAX </a><a data-track-click="{&quot;mod&quot;:&quot;popu_626&quot;,&quot;con&quot;:&quot;JAXP&quot;}" class="tag-link" href="http://so.csdn.net/so/search/s.do?q=JAXP&amp;t=blog" target="_blank">JAXP </a><a data-track-click="{&quot;mod&quot;:&quot;popu_626&quot;,&quot;con&quot;:&quot;XML Pull&quot;}" class="tag-link" href="http://so.csdn.net/so/search/s.do?q=XML Pull&amp;t=blog" target="_blank">XML Pull </a>
<span class="article_info_click">更多</span></span>
<div class="tags-box space">
<span class="label">个人分类:</span>
<a class="tag-link" href="https://blog.csdn.net/zhongkelee/article/category/6278290" target="_blank">JavaEE </a>
</div>
</div>
<div class="operating">
</div>
</div>
</div>
</div>
<article>
<div id="article_content" class="article_content clearfix csdn-tracking-statistics" data-pid="blog" data-mod="popu_307" data-dsm="post">
<div class="article-copyright">
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhongkelee/article/details/51737710 </div>
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f76675cdea.css">
<div class="htmledit_views">

前言

本文接着上一篇博客进行XML文档解析处理语法的介绍。在上一篇博客XML语法中我们提到了,XML技术在企业中主要应用在存储、传输数据和作为框架的配置文件这两块领域。本博客介绍的技术主要就是应用在通过XML进行存储和传输数据这一块。大致分为:JAXP
DOM 解析、JAXP SAX 解析、XML PULL 进行 STAX 解析这三个方面。

简介

使用xml 存储和传输数据

1、通过程序生成xml

2、读取xml中数据 ---- xml 解析

XML解析方式有三种:DOM、SAX、StAX

三种解析方式对应着三种解析思想,表述如下。

什么是DOM、SAX、StAX ?

DOM Document Object Model ----- 文档对象模型 
DOM思想: 将整个xml 加载内存中,形成文档对象,所有对xml操作都对内存中文档对象进行
DOM 是官方xml解析标准
* 所以DOM是所有开发语言都支持的 ---- Java、JavaScript 都支持DOM

SAX Simple API for XML ----- XML 简单 API

程序员为什么发明sax解析方式?? 当xml 文档非常大,不可能将xml所有数据加载到内存 

SAX 思想:一边解析 ,一边处理,一边释放内存资源 ---- 不允许在内存中保留大规模xml 数据

StAX The Stream API for XML ----- XML 流 API ---- JDK6.0新特性

STAX 是一种 拉模式 XML 解析方式,SAX 是一种 推模式 XML 解析方式(SAX性能不如STAX,STAX技术较新)

注解:

push模式:由服务器为主导,向客户端主动发送数据( 推送 )

pull模式: 由客户端为主导,主动向服务器申请数据( 轮询 )

三种解析开发包

掌握了三种思想后,程序员在实际开发中,使用已经开发好工具包 ----- JAXP 、DOM4j 、XML PULL

注解:

解析方式 与 解析开发包 关系?

解析方式是解析xml 思想,没有具体代码

解析开发包是解析xml思想具体代码实现。

JAXP 是sun官方推出实现技术 同时支持 DOM SAX STAX
DOM4j 是开源社区开源框架  支持 DOM 解析方式
XML PULL Android 移动设备内置xml 解析技术 支持 STAX 解析方式

DOM和SAX/STAX区别

|--DOM 支持回写
      |--会将整个XML载入内存,以树形结构方式存储
      |--XML比较复杂的时候,或者当你需要随机处理文档中数据的时候不建议使用

|--SAX/STAX
      |--相比DOM是一种更为轻量级的方案
      |--采用穿行方法读取 -- 文件输入流(字节、字符)读取
      |--编程较为复杂
      |--无法再读取过程中修改XML数据

注意:

当SAX和STAX 读取xml数据时,如果读取到内存数据不释放 ----- 内存中将存在整个xml文档数据(这种情况下就类似DOM 可以支持修改和回写)

DOM、SAX、STAX 在实际开发中选择?

在javaee日常开发中 ---- 优先使用DOM (编程简单)

当xml 文档数据非常多,不可能使用DOM ---造成内存溢出  ------ 优先使用STAX 

移动开发 使用 STAX ---- Android XML PULL

JAXP DOM 解析

JAXP开发包简介

JAXP(Java API for XML Processing):

    DOM、SAX、STAX 只是XML解析方式,没有API

    JAXP是 Sun 提供的一套XML解析API,它很好的支持DOM和SAX解析方式,JDK6.0开始支持STAX解析方式,JAXP 开发包是JavaSE的一部分,它由javax.xml、org.w3c.dom 、org.xml.sax 包及其子包组成,在 javax.xml.parsers 包中,定义了几个工厂类,程序员调用这些工厂类,可以得到对xml文档进行解析的 DOM 或 SAX 的解析器对象。

JAXP 开发 进行 xml解析 软件包: 
javax.xml.parsers  存放 DOM 和 SAX 解析器 
javax.xml.stream  存放 STAX 解析相关类 
org.w3c.dom 存放DOM解析时 数据节点类
org.xml.sax 存放SAX解析相关工具类

DOM解析模型

DOM 是以层次结构组织的节点或信息片断的集合,是 XML 数据的一种树型表示
XML文档中所有的元素、属性、文本都会被解析成node节点 ---- 从而在内存中形成XML文档树型模型 ---- 所有的解析操作都围绕着这个模型进行
(属性节点不属于任何节点的父节点或者子节点!)
节点之间关系:parent、children、sibling(兄弟)


DOM 解析快速入门

1、创建 xml 文档 books.xml 
在企业实际开发中,为了简化xml 生成和解析 ---- xml 数据文件通常不使用约束的

2、使用DOM解析xml 
将整个xml文档加载到内存中 : 工厂 --- 解析器 ---解析加载 


  1. DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();//工厂
  2. DocumentBuilder builder = builderFactory.newDocumentBuilder();//解析器
  3. Document document = builder.parse("books.xml");//装载进内存

3、Document通过 getElementsByTagName("")  获得 指定标签的节点集合 NodeList

通过 NodeList 提供 getLength 和 item遍历 节点集合


  1. 遍历ArrayList
  2. for (int i=0;i<arraylist.size();i++){
  3. arraylist.get(i);
  4. }
  5. 遍历NodeList
  6. for (int i=0;i<nodelist.getLength();i++){
  7. nodelist.item(i); ----- 将遍历的每个节点转换子接口类型
  8. }

什么是 Node? 

对于xml 来说,xml所有数据都是node节点 (元素节点、属性节点、文本节点、注释节点、CDATA节点、文档节点)

Element Attr Text Comment CDATASection Document  ----- 都是 Node 子接口

、Node对象提供了一系列常量来代表节点的类型(查看org.w3c.dom.Node接口源码):

当开发人员获得某个Node类型后,就可以把Node节点转换成相应的节点对象(Element、Attr、Text)。

注解:

node有三个通用API : 
      |--getNodeName():返回节点的名称
      |--getNodeType():返回节点的类型 ---- ELEMENT_NODE=1、ATTRIBUTE_NODE=2、TEXT_NODE=3
      |--getNodeValue():返回节点的值  ---- 所有元素节点value都是 null

另外,对于元素节点ELEMENT来说:

|--获得元素节点中的属性值
      |--element.getAttribute(属性名称)
|--获得元素节点内部文本内容
      |--element.getTextContent()
      |--element.getFirstChild().getNodeValue()

代码示例:

books.xml:


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <books>
  3. <book>
  4. <name>java编程基础</name>
  5. <price>80</price>
  6. </book>
  7. <book>
  8. <name>java高级应用</name>
  9. <price>100</price>
  10. </book>
  11. </books>

测试程序:


  1. package cn.itcast.dom.jaxp;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import javax.xml.parsers.DocumentBuilder;
  5. import javax.xml.parsers.DocumentBuilderFactory;
  6. import javax.xml.transform.dom.DOMSource;
  7. import javax.xml.transform.stream.StreamResult;
  8. import org.junit.Test;
  9. import org.w3c.dom.Document;
  10. import org.w3c.dom.Element;
  11. import org.w3c.dom.Node;
  12. import org.w3c.dom.NodeList;
  13. public class DOMTest {
  14. @Test
  15. // 查询 java编程基础 书 售价
  16. public void demo3() throws Exception {
  17. // 装载xml 加载内存 --- Document对象
  18. DocumentBuilderFactory builderFactory = DocumentBuilderFactory
  19. .newInstance();
  20. DocumentBuilder builder = builderFactory.newDocumentBuilder();
  21. Document document = builder.parse("books.xml");
  22. // 利用全局查询 锁定 每个name节点
  23. NodeList nodelist = document.getElementsByTagName("name");
  24. for (int i = 0; i < nodelist.getLength(); i++) {
  25. Element name = (Element) nodelist.item(i);
  26. if (name.getTextContent().equals("java编程基础")) {
  27. // 图书 找到了
  28. // price 是 name 节点 兄弟的兄弟,三个换行符也是子节点
  29. Element price = (Element) name.getNextSibling()
  30. .getNextSibling();
  31. System.out.println(price.getTextContent());
  32. }
  33. }
  34. }
  35. @Test
  36. // 查询 java编程基础 书 售价
  37. public void demo2() throws Exception {
  38. // 装载xml 加载内存 --- Document对象
  39. DocumentBuilderFactory builderFactory = DocumentBuilderFactory
  40. .newInstance();
  41. DocumentBuilder builder = builderFactory.newDocumentBuilder();
  42. Document document = builder.parse("books.xml");
  43. // 全局查询 作为程序 切入
  44. NodeList nodelist = document.getElementsByTagName("book");
  45. // 遍历 强制转换 Element
  46. for (int i = 0; i < nodelist.getLength(); i++) {
  47. Element book = (Element) nodelist.item(i);
  48. // 找 哪个 book 节点 当中 name 节点值 java编程基础 ---- 查找book的name 子节点
  49. NodeList chidren = book.getChildNodes();
  50. //System.out.println(chidren.getLength());//注意:回车、空格也是子元素
  51. Element name = (Element) chidren.item(1); // book的第二个子节点就是name
  52. if (name.getTextContent().equals("java编程基础")) {
  53. // 当前for循环 这本书 是目标图书
  54. // 打印图书价格 price 是 book 第四个子节点
  55. Element price = (Element) chidren.item(3);
  56. System.out.println(price.getTextContent());
  57. }
  58. }
  59. }
  60. @Test
  61. public void demo1() throws Exception {
  62. // 通过DOM 解析 XML --- 载入整个xml 工厂 -- 解析器 --- 加载
  63. // 构造工厂
  64. DocumentBuilderFactory builderFactory = DocumentBuilderFactory
  65. .newInstance();
  66. // 通过工厂 获得解析器
  67. DocumentBuilder builder = builderFactory.newDocumentBuilder();
  68. // 使用解析器 加载 xml文档
  69. Document document = builder.parse("books.xml");
  70. // Document代表整个xml 文档,通过操作Document,操作xml数据
  71. // 将所有图书名称打印出来
  72. // 这里 nodelist 代表节点的集合
  73. // 查询所有 name标签
  74. NodeList nodelist = document.getElementsByTagName("name");
  75. // 遍历集合中 所有 node
  76. System.out.println("图书name节点数量:" + nodelist.getLength());
  77. for (int i = 0; i < nodelist.getLength(); i++) {
  78. // 获得每个 node 节点
  79. Node node = nodelist.item(i); // 这里每个node 都是 <name></name> ---- 元素
  80. Element e = (Element) node; // 将 节点转换为 子类型 节点
  81. System.out.println(e.getNodeName()); // 节点元素名称
  82. System.out.println(e.getNodeType()); // 节点元素 类型
  83. System.out.println(e.getNodeValue()); // 节点元素 值
  84. // 输出 name 元素 子节点文本节点值
  85. System.out.println(e.getFirstChild().getNodeValue());
  86. System.out.println(e.getTextContent());
  87. System.out.println("------------------------------------");
  88. }
  89. }
  90. }

DOM 编程思路小结

1、装载XML文档 ---- Document (工厂--解析器--解析加载)

2、Document 获得指定元素 ----- getElementsByTagName (返回 NodeList)

3、遍历NodeList 获得 每个 Node

4、将每个Node 强制转换 Element 

5、通过元素节点API 操作属性和文本内容 

      |--getAttribute  获得属性值

      |--getTextContent 获得元素内部文本内容

这其中,第一步是固定套路:


  1. DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();//工厂
  2. DocumentBuilder builder = builderFactory.newDocumentBuilder();//解析器
  3. Document document = builder.parse("books.xml");//装载进内存

DOM的增删改查 ---- CURD

XML元素查询

节点对象的查询总结:

先用全局查找锁定范围,再用相对关系查找 得到需要数据。

|--全局查找元素节点

      |--document.getElementByTagName
      |--document.getElementById( 需要带约束的XML)
|--相对节点位置查找节点
      |--getChildNodes():返回这个节点的所有子节点列表
      |--getFirstChild():返回这个节点的第一个子节点
      |--getParentNode():返回这个节点的父节点对象
      |--getNextSibling():返回这个节点的下一个兄弟节点(注意空白也是节点)
      |--getPreviousSibling():返回这个节点的前一个兄弟节点

注意:getElementById 方法 必须用于带有约束 xml文档中 !!!!!!!

例如:

books.xml:


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE books [
  3. <!ELEMENT books (book+) >
  4. <!ELEMENT book (name,price) >
  5. <!ELEMENT name (#PCDATA) >
  6. <!ELEMENT price (#PCDATA) >
  7. <!ATTLIST book
  8. id ID #REQUIRED <!--就是这个东西-->
  9. >
  10. ]>
  11. <books>
  12. <book id="b001">
  13. <name>java编程基础</name>
  14. <price>80</price>
  15. </book>
  16. <book id="b002">
  17. <name>java高级应用</name>
  18. <price>100</price>
  19. </book>
  20. </books>

getElementById 代码示例:


  1. package cn.itcast.dom.jaxp;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import javax.xml.parsers.DocumentBuilder;
  5. import javax.xml.parsers.DocumentBuilderFactory;
  6. import javax.xml.transform.Transformer;
  7. import javax.xml.transform.TransformerFactory;
  8. import javax.xml.transform.dom.DOMSource;
  9. import javax.xml.transform.stream.StreamResult;
  10. import org.junit.Test;
  11. import org.w3c.dom.Document;
  12. import org.w3c.dom.Element;
  13. import org.w3c.dom.Node;
  14. import org.w3c.dom.NodeList;
  15. public class DOMTest {
  16. @Test
  17. // getElementById 用法 --- 查找 id b002 图书 名称
  18. public void demo4() throws Exception {
  19. // 装载xml 加载内存 --- Document对象
  20. DocumentBuilderFactory builderFactory = DocumentBuilderFactory
  21. .newInstance();
  22. DocumentBuilder builder = builderFactory.newDocumentBuilder();
  23. Document document = builder.parse("books.xml");
  24. // 直接通过id 查找 ----- 文档必须使用 约束 --- 不用约束xml文档 不能 使用getElementById
  25. Element book = document.getElementById("b002");
  26. System.out.println(book);
  27. System.out.println(book.getChildNodes().item(1).getTextContent());
  28. }

所有开发语言默认支持DTD,当使用Schema时,单独编程导入schema !

如何对xml文件进行schema约束?


  1. DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
  2. SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
  3. StreamSource ss = new StreamSource("books.xsd");
  4. Schema schema = factory.newSchema(ss);
  5. builderFactory.setSchema(schema);
  6. builderFactory.setNamespaceAware(true);

XML回写

XML DOM 增加 、修改 和 删除操作 ------ 操作 内存中文档对象 ---- 操作内存结束后要回写进某一文件中

更新XML文档步骤:
|--javax.xml.transform包中的Transformer类用于把代表XML文件的Document对象转换成XML格式进行输出
|--Transformer对象通过TransformerFactory获得
|--Transformer类通过transform方法完成转换操作,该方法接收一个源和一个目的地。我们可以通过:
      |--javax.xml.transform.dom.DOMSource类来关联要转换的document对象, 
      |--javax.xml.transform.stream.StreamResult 对象来表示数据的目的地。

代码示例:


  1. package cn.itcast.dom.jaxp;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import javax.xml.parsers.DocumentBuilder;
  5. import javax.xml.parsers.DocumentBuilderFactory;
  6. import javax.xml.transform.Transformer;
  7. import javax.xml.transform.TransformerFactory;
  8. import javax.xml.transform.dom.DOMSource;
  9. import javax.xml.transform.stream.StreamResult;
  10. import org.junit.Test;
  11. import org.w3c.dom.Document;
  12. import org.w3c.dom.Element;
  13. import org.w3c.dom.Node;
  14. import org.w3c.dom.NodeList;
  15. public class DOMTest {
  16. @Test
  17. // 将 books.xml 加载内存中,将文档内容写入另一个xml books_bak.xml(回写)
  18. public void demo5() throws Exception, IOException {
  19. // 将 文档 载入内存
  20. DocumentBuilderFactory builderFactory = DocumentBuilderFactory
  21. .newInstance();
  22. DocumentBuilder builder = builderFactory.newDocumentBuilder();
  23. Document document = builder.parse("books.xml");
  24. // 回写xml 用到 Transformer
  25. TransformerFactory transformerFactory = TransformerFactory
  26. .newInstance();
  27. Transformer transformer = transformerFactory.newTransformer();
  28. DOMSource domSource = new DOMSource(document);// 用document构造数据源
  29. StreamResult result = new StreamResult(new File("books_bak.xml"));
  30. transformer.transform(domSource, result);
  31. }
  32. }

其实你可以发现回写也是固定套路:


  1. TransformerFactory transformerFactory = TransformerFactory.newInstance();
  2. Transformer transformer = transformerFactory.newTransformer();
  3. DOMSource domSource = new DOMSource(document);// 用document构造数据源
  4. StreamResult result = new StreamResult(new File("books_bak.xml"));
  5. transformer.transform(domSource, result);

XML元素添加

|--创建节点元素
      |--document.createXXX()创建节点
|--将节点元素加入指定位置
      |--element.getDocumentElement()获得根节点
      |--element.appendChild(org.w3c.dom.Node)添加节点
|--回写XML

XML元素修改

|--加载xml到内存
|--查询到指定元素 
|--修改元素的属性值
      |--element.setAttribute(name,value);
|--修改元素内文本内容
      |--element.setTextContent(value);
|--回写XML

XML元素删除

|--删除节点.getParentNode().removeChild(删除节点)
      (删除必须通过父节点、注意每次删完之后修复nodelist长度!)

代码示例:


  1. package cn.itcast.dom.jaxp;
  2. import java.io.File;
  3. import javax.xml.parsers.DocumentBuilder;
  4. import javax.xml.parsers.DocumentBuilderFactory;
  5. import javax.xml.transform.Transformer;
  6. import javax.xml.transform.TransformerFactory;
  7. import javax.xml.transform.dom.DOMSource;
  8. import javax.xml.transform.stream.StreamResult;
  9. import org.junit.Test;
  10. import org.w3c.dom.Document;
  11. import org.w3c.dom.Element;
  12. import org.w3c.dom.NodeList;
  13. /**
  14. * CURD create update read delete
  15. *
  16. * @author seawind
  17. *
  18. */
  19. public class DOMCURDTest {
  20. @Test
  21. // 删除所有 java书名 ----- 图书
  22. public void testDelete() throws Exception {
  23. // 加载xml 到内存
  24. DocumentBuilderFactory builderFactory = DocumentBuilderFactory
  25. .newInstance();
  26. DocumentBuilder builder = builderFactory.newDocumentBuilder();
  27. Document document = builder.parse("books.xml");
  28. NodeList nodelist = document.getElementsByTagName("name");
  29. for (int i = 0; i < nodelist.getLength(); i++) {
  30. Element name = (Element) nodelist.item(i);
  31. if (name.getTextContent().contains("java")) {
  32. // 这本书删除 --- 通过name 获得图书
  33. Element book = (Element) name.getParentNode();
  34. // 删除 必须 通过父节点
  35. book.getParentNode().removeChild(book);
  36. i--; // 修复list长度
  37. }
  38. }
  39. // 回写
  40. TransformerFactory transformerFactory = TransformerFactory
  41. .newInstance();
  42. Transformer transformer = transformerFactory.newTransformer();
  43. DOMSource domSource = new DOMSource(document);// 用document构造数据源
  44. StreamResult result = new StreamResult(new File("books_bak.xml"));
  45. transformer.transform(domSource, result);
  46. }
  47. @Test
  48. // 将 java高级应用 价格上调 20%
  49. public void testUpdate() throws Exception {
  50. // 加载xml 到内存
  51. DocumentBuilderFactory builderFactory = DocumentBuilderFactory
  52. .newInstance();
  53. DocumentBuilder builder = builderFactory.newDocumentBuilder();
  54. Document document = builder.parse("books.xml");
  55. // 查找 java高级应用书
  56. NodeList nodelist = document.getElementsByTagName("name");
  57. for (int i = 0; i < nodelist.getLength(); i++) {
  58. Element name = (Element) nodelist.item(i);
  59. if (name.getTextContent().equals("java高级应用")) {
  60. // 找到了 --- 获得价格节点
  61. Element price = (Element) name.getNextSibling()
  62. .getNextSibling();
  63. double money = Double.parseDouble(price.getTextContent());
  64. money = money * 1.2;
  65. price.setTextContent(money + "");
  66. }
  67. }
  68. // 回写
  69. TransformerFactory transformerFactory = TransformerFactory
  70. .newInstance();
  71. Transformer transformer = transformerFactory.newTransformer();
  72. DOMSource domSource = new DOMSource(document);// 用document构造数据源
  73. StreamResult result = new StreamResult(new File("books_bak.xml"));
  74. transformer.transform(domSource, result);
  75. }
  76. @Test
  77. // 向xml 添加一个 book元素
  78. public void testAdd() throws Exception {
  79. // 1 将原来 books.xml 加载到内容
  80. DocumentBuilderFactory builderFactory = DocumentBuilderFactory
  81. .newInstance();
  82. DocumentBuilder builder = builderFactory.newDocumentBuilder();
  83. Document document = builder.parse("books.xml");
  84. // 2、添加节点 创建节点 books
  85. Element newBook = document.createElement("book"); // <book></book>
  86. newBook.setAttribute("id", "b003");
  87. // 创建name节点
  88. Element newName = document.createElement("name"); // <name></name>
  89. newName.setTextContent("编程高手秘笈");
  90. // 将 新 name 放入 新 book
  91. newBook.appendChild(newName);
  92. // 3、添加节点到指定位置 ---- 获得books根节点
  93. Element root = document.getDocumentElement();
  94. root.appendChild(newBook);
  95. // 4、回写xml
  96. TransformerFactory transformerFactory = TransformerFactory
  97. .newInstance();
  98. Transformer transformer = transformerFactory.newTransformer();
  99. DOMSource domSource = new DOMSource(document);// 用document构造数据源
  100. StreamResult result = new StreamResult(new File("books_bak.xml"));
  101. transformer.transform(domSource, result);
  102. }
  103. }

DOM总结:



JAXP SAX 解析

SAX 和 STAX 都是 基于事件驱动 ----- SAX推模式 STAX拉模式

SAX解析处理器的常用事件

|--DefaultHandler类(在 org.xml.sax.helpers 软件包中)来实现所有这些回调,并提供所有回调方法默认的空实现 
      |--startDocument()  ---- 文档开始事件
      |--startElement() ---- 元素开始事件
      |--characters() ---- 文本元素事件
      |--endElement() ---- 元素结束事件
      |--endDocument()  ----- 文档结束事件

SAX解析原理

SAX和DOM不同
DOM解析器 ---- 将整个XML文档全部加载到内存,返回文档对象Document
      解析器DocumentBuilder ---- Document document = builder.parse(file)
SAX解析器 ---- 一边读取XML一边解析一边处理,并没有返回值
      解析器SAXParser ---- 将XML文档和文档解析处理器(DefaultHandler及其子类)同时传递给SAX解析器 ---- 解析器调用处理器相应的事件处理方法来处理文档

为什么说SAX是推模式解析? 解析器控制xml文件解析,由解析器调用相应事件方法
由位于服务器端的解析器内部主导的事件方法调用 ---- 推模式

SAX解析器采用了基于事件的模型,它在解析XML文档的时候可以触发一系列的事件,发生相应事件时,将调用一个回调方法,例如:


  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <config>
  3. <server>UNIX</server>
  4. </config>

依次触发的事件:

Start document
Start element (config)
Characters (whitespace)
Start element (server)
Characters (UNIX)
End element (server)
Characters (whitespace)
End element (config)
End document

代码示例:

server.xml:


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <config>
  3. <server id="s100">UNIX</server>
  4. </config>

SAX解析:


  1. package cn.itcast.sax.jaxp;
  2. import javax.xml.parsers.SAXParser;
  3. import javax.xml.parsers.SAXParserFactory;
  4. import org.xml.sax.Attributes;
  5. import org.xml.sax.SAXException;
  6. import org.xml.sax.helpers.DefaultHandler;
  7. /**
  8. * 编写sax解析xml 实例
  9. *
  10. * @author seawind
  11. *
  12. */
  13. public class SAXTest {
  14. public static void main(String[] args) throws Exception {
  15. // 1、工厂
  16. SAXParserFactory factory = SAXParserFactory.newInstance();
  17. // 2、通过工厂获得解析器
  18. SAXParser parser = factory.newSAXParser();
  19. // 3 、创建 Handler
  20. MyHandler handler = new MyHandler();
  21. // 4、将xml 文档 和 handler 同时传递给 解析器
  22. parser.parse("server.xml", handler);
  23. }
  24. }
  25. /**
  26. * 继承 DefaultHandler 重写 5 个事件方法
  27. *
  28. * @author seawind
  29. *
  30. */
  31. class MyHandler extends DefaultHandler {
  32. @Override
  33. public void startDocument() throws SAXException {
  34. System.out.println("start document...");
  35. }
  36. @Override
  37. public void startElement(String uri, String localName, String qName,
  38. Attributes attributes) throws SAXException {
  39. System.out.println("start element(" + qName + ")...");
  40. // 打印server元素 id 属性 --- 判断当前开始元素是 server
  41. if (qName.equals("server")) {
  42. System.out.println("id属性的值:" + attributes.getValue("id"));
  43. }
  44. }
  45. @Override
  46. public void characters(char[] ch, int start, int length)
  47. throws SAXException {
  48. String content = new String(ch, start, length);
  49. System.out.println("characters: " + content);
  50. }
  51. @Override
  52. public void endElement(String uri, String localName, String qName)
  53. throws SAXException {
  54. System.out.println("end element(" + qName + ")...");
  55. }
  56. @Override
  57. public void endDocument() throws SAXException {
  58. System.out.println("end document...");
  59. }
  60. }

使用SAX方式解析XML步骤

|--使用SAXParserFactory创建SAX解析工厂
      |--SAXParserFactory spf = SAXParserFactory.newInstance();
|--通过SAX解析工厂得到解析器对象
      |--SAXParser sp = spf.newSAXParser();
|--通过解析器对象解析xml文件
      |--sp.parse("book.xml“,new XMLContentHandler());
      |--这里的XMLContentHandler 继承 DefaultHandler
            |--在startElement() endElement() 获得 开始和结束元素名称
            |--在characters() 获得读取到文本内容
            |--在startElement() 读取属性值

XML PULL 解析

STAX 拉模式xml 解析方式 ----- 客户端程序,自己控制xml事件,主动调用相应事件方法

XML PULL 解析器开发包简介

当使用XML PULL,如果使用Android系统,系统内置无需下载任何开发包;如果想JavaSE、JavaEE使用pull解析技术下载单独pull 开发工具包。、

xpp3 ----- XML Pull Parser 3 是pull API 代码实现

使用pull 解析器
1、去网站上 下载 pull 解析器的实现 xpp3 (Android 内置)
2、将 xpp3-1.1.3.4.C.jar 导入 java工程 
良好习惯:要导入jar包应当位于当前工程内部。
方法:在工程内新建lib文件夹,将jar复制过来,然后将pull解析器xpp3.jar包添加至Java Build Path (Libraries--Add JARs 或右键jar包 Add to Build Path),这样pull解析器才能使用。
注解:jar 包就是.class文件 集合压缩包 (采用zip格式压缩)
3、创建pull 解析器 ---- XmlPullParser
4、将xml 文档内容传递 pull 解析器
5、需要客户端程序手动完成解析,XmlPullParser存放解析方法next(),用于解析器解析下一事件

STAX解析原理

Pull解析器 使用 stax 解析方式 ---- 拉模式解析

SAX解析器当接收到XML文件内容,服务器端解析器SAXParser自动开始解析,自动解析过程中调用处理器相应方法 ---- 推模式
Pull采用将xml文档传递解析器,解析器XmlPullParser不会自动解析,需要手动通过next触发文档解析事件,在客户端代码中获取当前事件 ,从而调用相应事件处理方法。

为什么 STAX 解析方式 效率 好于 SAX ?

1、SAX 无选择性的,所有事件都会处理的解析方式,解析器控制事件的调用;StAX由用户自主控制需要处理事件类型以及事件的调用。
2、在使用Stax进行数据解析时,随时终止解析。

使用XML Pull解析 XML

|--参考官方文档
      |--http://www.xmlpull.org/v1/download/unpacked/doc/quick_intro.html
      |--Xpp3 XmlPullParser javadoc
|--关键代码
      |--创建解析器工厂
            |--XmlPullParserFactory factory =  XmlPullParserFactory.newInstance();
            |--factory.setNamespaceAware(true); 
      |--根据工厂创建解析器
            |--XmlPullParser xpp = factory.newPullParser(); 
      |--读取xml文件
            |--xpp.setInput(inStream, "UTF-8");
      |--当前节点事件类型
            |--int eventType = xpp.getEventType();
      |--下一个节点事件
            |--eventType = xpp.next();
      |--获得元素名称
            |--xpp.getName();
      |--获得标签属性值
            |--xpp.getAttributeValue
      |--获得标签后面文本内容
            |--xpp.nextText();

代码示例:

books.xml:


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE books [
  3. <!ELEMENT books (book+) >
  4. <!ELEMENT book (name,price) >
  5. <!ELEMENT name (#PCDATA) >
  6. <!ELEMENT price (#PCDATA) >
  7. <!ATTLIST book
  8. id ID #REQUIRED <!--就是这个东西-->
  9. >
  10. ]>
  11. <books>
  12. <book id="b001">
  13. <name>java编程基础</name>
  14. <price>80</price>
  15. </book>
  16. <book id="b002">
  17. <name>java高级应用</name>
  18. <price>100</price>
  19. </book>
  20. <book id="boo3">
  21. <name>编程高手秘笈</name>
  22. <price>200</price>
  23. </book>
  24. </books>

遍历代码实例、查询某本书的价格:


  1. package cn.itcast.stax.pull;
  2. import java.io.FileInputStream;
  3. import org.junit.Test;
  4. import org.xmlpull.v1.XmlPullParser;
  5. import org.xmlpull.v1.XmlPullParserFactory;
  6. /**
  7. * 通过 pull 解析器 解析 xml
  8. *
  9. * @author seawind
  10. *
  11. */
  12. public class PullTest {
  13. @Test
  14. // 通过 pull 解析技术 查看 "编程高手秘笈" 价格
  15. public void demo2() throws Exception {
  16. // 1. 创建 pull 解析器
  17. XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
  18. XmlPullParser parser = factory.newPullParser();
  19. // 2. 将 xml文档传递 解析器
  20. parser.setInput(new FileInputStream("books.xml"), "utf-8");
  21. // 通过循环 驱动事件解析
  22. int event;
  23. // 查找name 标识位
  24. boolean isFound = false;
  25. while ((event = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
  26. // 获得 开始元素 name
  27. if (event == XmlPullParser.START_TAG
  28. && parser.getName().equals("name")) {
  29. // 获得元素后面文本
  30. String bookname = parser.nextText();
  31. if (bookname.equals("编程高手秘笈")) {
  32. isFound = true;
  33. // 这本书就是我要找到
  34. // parser.next();
  35. // System.out.println(parser.getEventType());
  36. // parser.next(); // price 开始
  37. // System.out.println(parser.getEventType());
  38. // String money = parser.nextText();//太麻烦,用标识位简单
  39. // System.out.println(money);
  40. }
  41. }
  42. if (event == XmlPullParser.START_TAG
  43. && parser.getName().equals("price") && isFound) {
  44. System.out.println(parser.nextText());
  45. break;
  46. }
  47. parser.next();
  48. }
  49. }
  50. @Test
  51. public void demo1() throws Exception {
  52. // 1、创建 xml pull 解析器
  53. // 工厂
  54. XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory
  55. .newInstance();
  56. // 通过工厂 获得解析器
  57. XmlPullParser parser = xmlPullParserFactory.newPullParser();
  58. // 2、将 xml 文件 传递 解析器
  59. parser.setInput(new FileInputStream("books.xml"), "utf-8");
  60. // pull 解析器用得是 拉模式 数据 解析
  61. int event;
  62. while ((event = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
  63. //System.out.println(event);
  64. // 打印哪个元素开始了 ---- 判断当前事件 是 元素开始事件
  65. if (event == XmlPullParser.START_TAG) {
  66. // 所有数据 从解析器 获得
  67. System.out.println(parser.getName() + "元素开始了...");
  68. }
  69. // 打印 哪个 元素 结束了
  70. if (event == XmlPullParser.END_TAG) {
  71. System.out.println(parser.getName() + "元素 结束了...");
  72. }
  73. // 处理下一个事件
  74. parser.next();
  75. }
  76. // parser.getEventType()获得当前事件类型
  77. // 可以通过查看XmlPullParser源码得到各常量代表意义
  78. // int event = parser.getEventType();
  79. //
  80. // System.out.println(event);//START_DOCUMENT = 0
  81. //
  82. // parser.next(); // 解析器解析下一个事件
  83. //
  84. // int event2 = parser.getEventType();
  85. //
  86. // System.out.println(event2);//START_TAG = 2
  87. //
  88. // parser.next();
  89. //
  90. // int event3 = parser.getEventType();
  91. //
  92. // System.out.println(event3);//TEXT = 4
  93. }
  94. }

XML PULL 生成XML文档

Pull 解析器 生成 xml 文档功能 ---- 通过 XmlSerializer 生成 xml 文档

解析xml :文档开始、元素开始、文本元素、元素结束、文档结束

生成xml :生成文档声明(文档开始),元素开始、文本内容、元素结束 、文档结束

代码示例:

1、生成简单xml

2、通过对象数据生成xml

3、通过对象List数据生成xml

      ---- 序列化 XmlSerializer


  1. package cn.itcast.stax.pull;
  2. import java.io.FileOutputStream;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import org.junit.Test;
  6. import org.xmlpull.v1.XmlPullParserFactory;
  7. import org.xmlpull.v1.XmlSerializer;
  8. import cn.itcast.domain.Company;
  9. /**
  10. * 生成 xml
  11. *
  12. * @author seawind
  13. *
  14. */
  15. public class SerializerTest {
  16. @Test
  17. // 根据 List<Company> 生成xml
  18. public void demo3() throws Exception {
  19. List<Company> companies = new ArrayList<Company>();
  20. Company company = new Company();
  21. company.setName("传智播客");
  22. company.setPnum(200);
  23. company.setAddress("西二旗软件园!");
  24. Company company2 = new Company();
  25. company2.setName("CSDN");
  26. company2.setPnum(1000);
  27. company2.setAddress("西二旗 软件园 ");
  28. companies.add(company);
  29. companies.add(company2);
  30. // 序列化对象
  31. XmlSerializer serializer = XmlPullParserFactory.newInstance()
  32. .newSerializer();
  33. // 设置输出文件
  34. serializer.setOutput(new FileOutputStream("company.xml"), "utf-8");
  35. serializer.startDocument("utf-8", true);
  36. serializer.startTag(null, "companies");
  37. // 遍历list集合
  38. for (Company c : companies) {
  39. serializer.startTag(null, "company");
  40. serializer.startTag(null, "name");
  41. serializer.text(c.getName());
  42. serializer.endTag(null, "name");
  43. serializer.startTag(null, "pnum");
  44. serializer.text(c.getPnum() + "");
  45. serializer.endTag(null, "pnum");
  46. serializer.startTag(null, "address");
  47. serializer.text(c.getAddress());
  48. serializer.endTag(null, "address");
  49. serializer.endTag(null, "company");
  50. }
  51. serializer.endTag(null, "companies");
  52. serializer.endDocument();
  53. }
  54. @Test
  55. // 根据company对象数据生成xml
  56. public void demo2() throws Exception {
  57. Company company = new Company();
  58. company.setName("传智播客");
  59. company.setPnum(200);
  60. company.setAddress("西二旗软件园!");
  61. /*
  62. * <company>
  63. *
  64. * <name>传智播客</name>
  65. *
  66. * <pnum>200</pnum>
  67. *
  68. * <address>西二旗软件园</address>
  69. *
  70. * </company>
  71. */
  72. // 获得序列化对象
  73. XmlSerializer serializer = XmlPullParserFactory.newInstance()
  74. .newSerializer();
  75. // 传递 输出目标文件 给序列化对象
  76. serializer.setOutput(new FileOutputStream("company.xml"), "utf-8");
  77. serializer.startDocument("utf-8", true);
  78. serializer.startTag(null, "company");
  79. serializer.startTag(null, "name");
  80. serializer.text(company.getName());
  81. serializer.endTag(null, "name");
  82. serializer.startTag(null, "pnum");
  83. serializer.text(company.getPnum() + "");
  84. serializer.endTag(null, "pnum");
  85. serializer.startTag(null, "address");
  86. serializer.text(company.getAddress());
  87. serializer.endTag(null, "address");
  88. serializer.endTag(null, "company");
  89. serializer.endDocument();
  90. }
  91. @Test
  92. public void demo1() throws Exception {
  93. // 获得XmlSerializer对象
  94. XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
  95. XmlSerializer serializer = factory.newSerializer();
  96. // 设置序列化输出文档
  97. serializer.setOutput(new FileOutputStream("company.xml"), "utf-8");
  98. // 文档开始
  99. serializer.startDocument("utf-8", true);
  100. // 元素开始
  101. serializer.startTag(null, "company"); // 没有命名空间 ,"" 或者 null
  102. // 文本元素
  103. serializer.text("传智播客");
  104. // 元素结束
  105. serializer.endTag(null, "company");
  106. // 文档结束
  107. serializer.endDocument();
  108. /*
  109. * <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
  110. *
  111. * <company>
  112. *
  113. * 传智播客
  114. *
  115. * </company>
  116. */
  117. }
  118. }

STAX的增删改查 ---- CURD

对xml文件通过pull解析器进行CURD操作原理:

当下问题:pull解析器封装List对象过程 ---- 如何将XML数据 --> List<Object>

将xml数据通过pull解析器生成List集合代码示例

company源码:


  1. package cn.itcast.domain;
  2. /**
  3. * 公司数据类
  4. *
  5. * @author seawind
  6. *
  7. */
  8. public class Company {
  9. private String name;
  10. private int pnum;//人数
  11. private String address;
  12. public String getName() {
  13. return name;
  14. }
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18. public int getPnum() {
  19. return pnum;
  20. }
  21. public void setPnum(int pnum) {
  22. this.pnum = pnum;
  23. }
  24. public String getAddress() {
  25. return address;
  26. }
  27. public void setAddress(String address) {
  28. this.address = address;
  29. }
  30. }

测试代码:


  1. package cn.itcast.stax.pull;
  2. import java.io.FileInputStream;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import org.junit.Test;
  6. import org.xmlpull.v1.XmlPullParser;
  7. import org.xmlpull.v1.XmlPullParserFactory;
  8. import cn.itcast.domain.Company;
  9. public class PullCURD {
  10. @Test
  11. // 将xml中数据 ---- List集合对象
  12. public void demo1() throws Exception {
  13. List<Company> companies = new ArrayList<Company>();
  14. Company company = null;//定义一个临时Company引用
  15. // 获得解析器
  16. XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
  17. XmlPullParser parser = factory.newPullParser();
  18. // 向解析器传入xml文件
  19. parser.setInput(new FileInputStream("company.xml"), "utf-8");
  20. // 遍历解析
  21. int event;
  22. while ((event = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
  23. if (event == XmlPullParser.START_TAG
  24. && parser.getName().equals("company")) {
  25. // company 开始 创建 company 对象
  26. company = new Company();
  27. }
  28. if (event == XmlPullParser.START_TAG
  29. && parser.getName().equals("name")) {
  30. // name 元素开始 -- 封装name属性
  31. company.setName(parser.nextText());
  32. }
  33. if (event == XmlPullParser.START_TAG
  34. && parser.getName().equals("pnum")) {
  35. company.setPnum(Integer.parseInt(parser.nextText()));
  36. }
  37. if (event == XmlPullParser.START_TAG
  38. && parser.getName().equals("address")) {
  39. company.setAddress(parser.nextText());
  40. }
  41. if (event == XmlPullParser.END_TAG
  42. && parser.getName().equals("company")) {
  43. // company 结束
  44. companies.add(company);
  45. }
  46. parser.next();
  47. }
  48. for (Company c : companies) {
  49. System.out.println(c.getName());
  50. System.out.println(c.getPnum());
  51. System.out.println(c.getAddress());
  52. System.out.println("----------------------");
  53. }
  54. }
  55. }

当内存中读取到List<Object>后,就可以CURD,最后序列化到XML文件中即可。

因为上述方法经常使用,所以建议在程序中抽取这两个方法 ----- 1. xml ---> List对象  2. List对象生成xml

* XmlPullUtils通用程序(反射+泛型) ------ 编写一个工具类 以后pull解析


  1. package cn.itcast.stax.pull;
  2. import java.io.FileInputStream;
  3. import java.io.FileOutputStream;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import org.xmlpull.v1.XmlPullParser;
  7. import org.xmlpull.v1.XmlPullParserFactory;
  8. import org.xmlpull.v1.XmlSerializer;
  9. import cn.itcast.domain.Company;
  10. /**
  11. * 工具类 抽取两个方法 :1、xml --> List 2 List --> XML
  12. *
  13. * @author seawind
  14. *
  15. */
  16. public class PullUtils {
  17. /**
  18. * 接收xml文件,返回 List集合
  19. *
  20. * @param fileName
  21. * @return
  22. * @throws Exception
  23. */
  24. public static List<Company> parseXml2List(String fileName) throws Exception {
  25. List<Company> companies = new ArrayList<Company>();
  26. // 获得解析器
  27. XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
  28. XmlPullParser parser = factory.newPullParser();
  29. // 设置 xml 输入文件
  30. parser.setInput(new FileInputStream(fileName), "utf-8");
  31. // 解析遍历
  32. int event;
  33. // 定义一个临时Company 引用
  34. Company company = null;
  35. while ((event = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
  36. // 将每个<company> 元素封装 Company对象
  37. // 1、在company开始时候,创建对象
  38. if (event == XmlPullParser.START_TAG
  39. && parser.getName().equals("company")) {
  40. company = new Company();
  41. }
  42. // 2、读取name元素时,向company对象中封装 name属性
  43. if (event == XmlPullParser.START_TAG
  44. && parser.getName().equals("name")) {
  45. company.setName(parser.nextText());
  46. }
  47. // 3、读取pnum元素时,向company对象保存pnum属性
  48. if (event == XmlPullParser.START_TAG
  49. && parser.getName().equals("pnum")) {
  50. company.setPnum(Integer.parseInt(parser.nextText()));
  51. }
  52. // 4、读取address元素,向company封装 address属性
  53. if (event == XmlPullParser.START_TAG
  54. && parser.getName().equals("address")) {
  55. company.setAddress(parser.nextText());
  56. }
  57. // 5、读取company元素结束时,将company对象加入集合
  58. if (event == XmlPullParser.END_TAG
  59. && parser.getName().equals("company")) {
  60. companies.add(company);
  61. }
  62. // 解析 下一个 事件
  63. parser.next();
  64. }
  65. return companies;
  66. }
  67. /**
  68. * 同时接收xml文件和List集合,将集合中数据写入xml文件中
  69. *
  70. * @param companies
  71. * @param fileName
  72. * @throws Exception
  73. */
  74. public static void serializeList2Xml(List<Company> companies,
  75. String fileName) throws Exception {
  76. // 获得序列化对象
  77. XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
  78. XmlSerializer serializer = factory.newSerializer();
  79. // 写文件之前,指定输出文件
  80. serializer.setOutput(new FileOutputStream(fileName), "utf-8");
  81. // 文档开始
  82. serializer.startDocument("utf-8", true);
  83. // 根元素开始 companies
  84. serializer.startTag(null, "companies");
  85. // 遍历集合List ,每个List中Company对象 生成一个片段
  86. for (Company company : companies) {
  87. // company 开始
  88. serializer.startTag(null, "company");
  89. // name属性开始
  90. serializer.startTag(null, "name");
  91. // 写入name数据
  92. serializer.text(company.getName());
  93. // name属性结束
  94. serializer.endTag(null, "name");
  95. // pnum 属性开始
  96. serializer.startTag(null, "pnum");
  97. // 写入pnum数据
  98. serializer.text(company.getPnum() + "");
  99. // pnum 属性结束
  100. serializer.endTag(null, "pnum");
  101. // address属性开始
  102. serializer.startTag(null, "address");
  103. // 写入 address值
  104. serializer.text(company.getAddress());
  105. // address属性结束
  106. serializer.endTag(null, "address");
  107. // company 结束
  108. serializer.endTag(null, "company");
  109. }
  110. // 根元素结束
  111. serializer.endTag(null, "companies");
  112. // 文档结束
  113. serializer.endDocument();
  114. }
  115. }

对内存中List进行CURD操作 


  1. package cn.itcast.stax.pull;
  2. import java.io.FileInputStream;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import org.junit.Test;
  6. import org.xmlpull.v1.XmlPullParser;
  7. import org.xmlpull.v1.XmlPullParserFactory;
  8. import cn.itcast.domain.Company;
  9. /**
  10. * 完成pull解析器CURD操作
  11. *
  12. * @author seawind
  13. *
  14. */
  15. public class PullCURD {
  16. @Test
  17. // CSDN 从列表删除
  18. public void testDelete() throws Exception {
  19. // 1、解析 xml 数据到内存 list
  20. List<Company> companies = PullUtils.parseXml2List("company.xml");
  21. // 2、从list集合中删除 csdn 的company对象
  22. for (Company company : companies) {
  23. if (company.getName().equals("CSDN")) {
  24. companies.remove(company);
  25. break;//不写break,会报并发错误
  26. }
  27. }
  28. // 3、回写xml
  29. PullUtils.serializeList2Xml(companies, "company_bak.xml");
  30. }
  31. @Test
  32. // 将传智播客人数 200%
  33. public void testUpdate() throws Exception {
  34. // 1、解析 xml 数据到内存 list
  35. List<Company> companies = PullUtils.parseXml2List("company.xml");
  36. // 2、增长传智播客人数 200%
  37. for (Company company : companies) {
  38. if (company.getName().equals("传智播客")) {
  39. company.setPnum(company.getPnum() * 2);
  40. }
  41. }
  42. // 3、回写xml
  43. PullUtils.serializeList2Xml(companies, "company_bak.xml");
  44. }
  45. @Test
  46. // 查询 csdn人数
  47. public void testSelect() throws Exception {
  48. // 1、解析 xml 数据到内存 list
  49. List<Company> companies = PullUtils.parseXml2List("company.xml");
  50. // 2 遍历集合对象
  51. for (Company company : companies) {
  52. if (company.getName().equals("CSDN")) {
  53. System.out.println(company.getPnum());
  54. }
  55. }
  56. }
  57. @Test
  58. // 向 company插入一个公司信息
  59. public void testAdd() throws Exception {
  60. // 1、解析 xml 数据到内存 list
  61. List<Company> companies = PullUtils.parseXml2List("company.xml");
  62. // 2、添加company对象
  63. Company company = new Company();
  64. company.setName("baidu");
  65. company.setPnum(5000);
  66. company.setAddress("百度大楼!");
  67. companies.add(company);
  68. // 3、将List对象回写xml
  69. PullUtils.serializeList2Xml(companies, "company_bak.xml");
  70. }
  71. @Test
  72. // 测试工具类 PullUtils中方法
  73. public void demo2() throws Exception {
  74. // 将 company.xml 复制 company_bak.xml
  75. // 解析获得集合
  76. List<Company> companies = PullUtils.parseXml2List("company.xml");
  77. // 将集合写入 company_bak.xml
  78. PullUtils.serializeList2Xml(companies, "company_bak.xml");
  79. }
  80. }

综合案例

平时的开发过程中,经常涉及到xml文件的解析,实现xml文件到java bean的转换。当前有个xml文件,在不允许使用第三方jar的情况下解析xml文件,并根据member节点建立member对象。文件格式如下:


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <root>
  3. <member>
  4. <name>Jack1</name>
  5. <age>18</age>
  6. <grade>70</grade>
  7. </member>
  8. <member>
  9. <name>Appl2</name>
  10. <age>20</age>
  11. <grade>70</grade>
  12. </member>
  13. <member>
  14. <name>Appl2</name>
  15. <age>20</age>
  16. <grade>70</grade>
  17. </member>
  18. <member>
  19. <name>Appl2</name>
  20. <age>20</age>
  21. <grade>72</grade>
  22. </member>
  23. <member>
  24. <name>Adpl2</name>
  25. <age>20</age>
  26. <grade>73</grade>
  27. </member>
  28. <member>
  29. <name>ccpl2</name>
  30. <age>20</age>
  31. <grade>75</grade>
  32. </member>
  33. <member>
  34. <name>bppl2</name>
  35. <age>20</age>
  36. <grade>75</grade>
  37. </member>
  38. </root>

在输入文件中(文件类型为xml文件),存放话务员的基本信息。该文件中的话务员信息是乱序并且有可能重复的,现在需要输出每位话务员的信息,对于重复的信息只能输出一次。要求如下:

1、需要把话务员信息使用集合类缓存起来,并且集合中的信息必须唯一(姓名+年龄唯一)。

2、输出话务员信息,输出格式为:姓名(年龄):成绩|姓名(年龄):成绩,依次按照成绩、姓名、年龄升序排列。

3、启动两个线程分别做如下处理:

线程一:对于话务员年龄小于(包含)18岁的,成绩统一加10分。并把话务员信息依次按照成绩、姓名、年龄升序的方式输出到一个队列中。队列的大小不能超过10个。

线程二: 现有两个分公司(A,B)依次选择话务员,如:A选择第一个话务员后,B再选择一个,依次类推,直到话务员被选完。最后,分别输出A,B两个分公司所选择的话务员信息,输出格式为:姓名(年龄):成绩|姓名(年龄):成绩,依次按照成绩、姓名、年龄升序排列。

控制台输出:

1、输出话务员信息,输出格式为:姓名(年龄):成绩|姓名(年龄):成绩,依次按照成绩、姓名、年龄升序排列。

2、输出分公司A选择的话务员信息,输出格式为:姓名(年龄):成绩|姓名(年龄):成绩,依次按照成绩、姓名、年龄升序排列。

3、输出分公司B选择的话务员信息,,输出格式为:姓名(年龄):成绩|姓名(年龄):成绩,依次按照成绩、姓名、年龄升序排列。

上面输出的结果为:

Appl2(20):70|Jack1(18):70|Adpl2(20):73|bppl2(20):75|ccpl2(20):75

Appl2(20):70|bppl2(20):75|Jack1(18):80

Adpl2(20):73|ccpl2(20):75

分析:

题目要求 XML 文档

1、将话务员数据从xml 文件 读取出来 ------ Java 对象 -----  很多 话务员 ----- Java对象 集合

* 集合中的信息必须唯一(姓名+年龄唯一) ---- java 对象使用姓名+年龄 排重

2、输出话务员信息 ---  依次按照成绩、姓名、年龄升序排列

Appl2(20):70|Jack1(18):70|Adpl2(20):73|bppl2(20):75|ccpl2(20):75

Appl2(20):70|Jack1(18):70|Adpl2(20):73|bppl2(20):75|ccpl2(20):75|

3、线程一:对于话务员年龄小于(包含)18岁的,成绩统一加10分。并把话务员信息依次按照成绩、姓名、年龄升序的方式输出到一个队列中。队列的大小不能超过10个。

4、线程二: 现有两个分公司(A,B)依次选择话务员 ,输出格式为:姓名(年龄):成绩|姓名(年龄):成绩,依次按照成绩、姓名、年龄升序排列。
注:

HashSet --- 排重集合 排重hashCode 和 equals

TreeSet ---- 即排重又排序集合 排重和排序 通过compareTo 

* 当排重和排序字段相同时 ---TreeSet ,否则不可以用TreeSet

Comparable<E>接口中的排序方法 ---- public int compareTo(E o):

前-后 升序

后-前 降序

代码示例:

Member.java:


  1. package mytest;
  2. public class Member implements Comparable<Member>, Cloneable {
  3. private String name;
  4. private int age;
  5. private int grade;
  6. @Override
  7. public int hashCode() {
  8. final int prime = 31;
  9. int result = 1;
  10. result = prime * result + age;
  11. result = prime * result + ((name == null) ? 0 : name.hashCode());
  12. return result;
  13. }
  14. @Override
  15. public boolean equals(Object obj) {
  16. if (this == obj)
  17. return true;
  18. if (obj == null)
  19. return false;
  20. if (getClass() != obj.getClass())
  21. return false;
  22. Member other = (Member) obj;
  23. if (age != other.age)
  24. return false;
  25. if (name == null) {
  26. if (other.name != null)
  27. return false;
  28. } else if (!name.equals(other.name))
  29. return false;
  30. return true;
  31. }
  32. public String getName() {
  33. return name;
  34. }
  35. public void setName(String name) {
  36. this.name = name;
  37. }
  38. public int getAge() {
  39. return age;
  40. }
  41. public void setAge(int age) {
  42. this.age = age;
  43. }
  44. @Override
  45. public int compareTo(Member o) {
  46. // 升序 当前元素 - 传入元素 值
  47. // 成绩升序
  48. int result1 = this.getGrade() - o.getGrade();
  49. if (result1 == 0) {
  50. // 姓名升序
  51. int result2 = this.name.compareTo(o.name);
  52. if (result2 == 0) {
  53. // 年龄升序
  54. int result3 = this.age - o.age;
  55. return result3;
  56. } else {
  57. return result2;
  58. }
  59. } else {
  60. return result1;
  61. }
  62. }
  63. public void setGrade(int grade) {
  64. this.grade = grade;
  65. }
  66. public int getGrade() {
  67. return grade;
  68. }
  69. @Override
  70. public Object clone() throws CloneNotSupportedException {
  71. return super.clone();
  72. }
  73. }

MemberTest.java


  1. package mytest;
  2. import java.util.ArrayList;
  3. import java.util.HashSet;
  4. import java.util.LinkedList;
  5. import java.util.List;
  6. import java.util.Queue;
  7. import java.util.Set;
  8. import java.util.TreeSet;
  9. import javax.xml.parsers.DocumentBuilder;
  10. import javax.xml.parsers.DocumentBuilderFactory;
  11. import org.w3c.dom.Document;
  12. import org.w3c.dom.Element;
  13. import org.w3c.dom.NodeList;
  14. public class MemberTest {
  15. public static void main(String[] args) throws Exception {
  16. // 第一问 读取xml数据进行排重
  17. // 将xml中数据 保存 Member对象集合
  18. Set<Member> members = new HashSet<Member>();
  19. // 读取xml --- JAXP DOM
  20. DocumentBuilderFactory builderFactory = DocumentBuilderFactory
  21. .newInstance();
  22. DocumentBuilder builder = builderFactory.newDocumentBuilder();
  23. Document document = builder.parse("member.xml");
  24. // 每一个<member>标签 对应 member
  25. NodeList nodeList = document.getElementsByTagName("member");
  26. for (int i = 0; i < nodeList.getLength(); i++) {
  27. Element memberElement = (Element) nodeList.item(i);
  28. Member member = new Member();
  29. // 第二个元素 name
  30. member.setName(memberElement.getChildNodes().item(1)
  31. .getTextContent());
  32. // 第四个元素 age
  33. member.setAge(Integer.parseInt(memberElement.getChildNodes()
  34. .item(3).getTextContent()));
  35. // 第六个元素 grade
  36. member.setGrade(Integer.parseInt(memberElement.getChildNodes()
  37. .item(5).getTextContent()));
  38. // 将 member存入集合
  39. members.add(member);
  40. }
  41. // 检查第一问结果:输出应该是5
  42. //System.out.println(members.size());
  43. // 第二问 ,对 Set中member数据进行排序
  44. Set<Member> sortMembers = new TreeSet<Member>();
  45. sortMembers.addAll(members);
  46. for (Member member : sortMembers) {
  47. System.out.print(member.getName() + "(" + member.getAge() + "):"
  48. + member.getGrade() + "|");
  49. }
  50. System.out.println();
  51. // 第三问
  52. // ,对于话务员年龄小于(包含)18岁的,成绩统一加10分。
  53. //并把话务员信息依次按照成绩、姓名、年龄升序的方式输出到一个队列中。
  54. //队列的大小不能超过10个
  55. new Thread(new MyThread1(sortMembers)).start();
  56. // 第四问 模拟选人
  57. Runnable a = new MyThread2(sortMembers, "A");
  58. Runnable b = new MyThread2(sortMembers, "B");
  59. new Thread(a).start();
  60. new Thread(b).start();
  61. }
  62. }
  63. class MyThread2 implements Runnable {
  64. private static List<Member> members;//static保证唯一
  65. private String companyName;
  66. private static boolean aturn = true;//static保证唯一
  67. public MyThread2(Set<Member> members, String companyName) {
  68. // 将set中元素转存List中,实现有序取出元素remove(index)
  69. this.members = new ArrayList<Member>();
  70. this.members.addAll(members);
  71. this.companyName = companyName;
  72. }
  73. @Override
  74. public void run() {
  75. synchronized (members) {
  76. while (members.size() != 0) {
  77. // System.out.println(companyName + "执行");
  78. if (aturn && companyName.equals("A")) {
  79. Member m = members.remove(0);
  80. System.out.println("A公司选择:" + m.getName());
  81. aturn = false;
  82. members.notify();
  83. try {
  84. System.out.println("A 等待");
  85. members.wait();
  86. } catch (InterruptedException e) {
  87. e.printStackTrace();
  88. }
  89. }
  90. if (!aturn && companyName.equals("B")) {
  91. Member m = members.remove(0);
  92. System.out.println("B公司选择:" + m.getName());
  93. aturn = true;
  94. members.notify();
  95. try {
  96. System.out.println("B 等待");
  97. members.wait();
  98. } catch (InterruptedException e) {
  99. e.printStackTrace();
  100. }
  101. }
  102. }
  103. }
  104. System.exit(0);
  105. }
  106. }
  107. class MyThread1 implements Runnable {
  108. private Set<Member> members;
  109. private Queue<Member> queue = new LinkedList<Member>();
  110. public MyThread1(Set<Member> members) {
  111. this.members = members;
  112. }
  113. @Override
  114. public void run() {
  115. // 加分排序
  116. Set<Member> sortMembers = new TreeSet<Member>();
  117. // 遍历原来的集合
  118. for (Member member : members) {
  119. if (member.getAge() <= 18) {
  120. // 加分 ---不能在原来对象里加分
  121. Member m = null;
  122. try {
  123. m = (Member) member.clone();
  124. m.setGrade(m.getGrade() + 10);
  125. sortMembers.add(m);
  126. } catch (CloneNotSupportedException e) {
  127. e.printStackTrace();
  128. }
  129. } else {
  130. // 不需要克隆
  131. sortMembers.add(member);
  132. }
  133. }
  134. for (Member member : sortMembers) {
  135. System.out.print(member.getName() + "(" + member.getAge() + "):"
  136. + member.getGrade() + "|");
  137. // 加入队列
  138. queue.add(member);
  139. }
  140. System.out.println();
  141. }
  142. }

				<script>
(function(){
function setArticleH(btnReadmore,posi){
var winH = $(window).height();
var articleBox = $("div.article_content");
var artH = articleBox.height();
if(artH > winH*posi){
articleBox.css({
'height':winH*posi+'px',
'overflow':'hidden'
})
btnReadmore.click(function(){
articleBox.removeAttr("style");
$(this).parent().remove();
})
}else{
btnReadmore.parent().remove();
}
}
var btnReadmore = $("#btn-readmore");
if(btnReadmore.length>0){
if(currentUserName){
setArticleH(btnReadmore,3);
}else{
setArticleH(btnReadmore,1.2);
}
}
})()
</script>
</article>
posted @
2018-11-05 15:02 
jobs-lgy 
阅读(...) 
评论(...) 
编辑 
收藏

JavaEE实战——XML文档DOM、SAX、STAX解析方式详解的更多相关文章

  1. 2.2 使用 JAXP 对XML文档进行SAX解析

    使用JAXP 对 XML文档进行 SAX解析: public class Demo1 { /** * 使用JAXP对XML文档进行SAX解析 * @throws Exception * @throws ...

  2. javaweb学习总结十二(JAXP对XML文档进行SAX解析)

    一:JAXP使用SAX方式解析XML文件 1:dom解析与sax解析异同点 2:sax解析特点 二:代码案例 1:xml文件 <?xml version="1.0" enco ...

  3. xml文档读取-SAX

    由于dom采用的是将xml文档加载入内存进行处理的方式,如果xml文档较大,则会导致加载时间过长,效率降低的情况,因此,sun公司在JAXP中又添加了对SAX的支持: SAX,全称Simple API ...

  4. Java高级特性 第14节 解析XML文档(2) - SAX 技术

    一.SAX解析XML文档 SAX的全称是Simple APIs for XML,也即XML简单应用程序接口.与DOM不同,SAX提供的访问模式是一种顺序模式,这是一种快速读写XML数据的方式.当使用S ...

  5. Java解析XML文档——dom解析xml

    一.前言 用Java解析XML文档,最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object M ...

  6. java解析xml文档(dom)

    DOM解析XML文档 读取本地的xml文件,通过DOM进行解析,DOM解析的特点就是把整个xml文件装载入内存中,形成一颗DOM树形结构,树结构是方便遍历和和操纵. DOM解析的特性就是读取xml文件 ...

  7. XML 文档定义有几种形式?它们之间有何本质区别?解析 XML 文档有哪几种方式?

    XML 文档定义分为 DTD 和 Schema 两种形式,二者都是对 XML 语法的约束,其 本质区别在于 Schema 本身也是一个 XML 文件,可以被 XML 解析器解析,而且 可以为 XML ...

  8. XML文档的生成和解析操作方法

    XML文档和JSon文档同为网络传输中的数据格式,JSon的解析和创建已经在新浪微博的使用中相当熟悉,故仅仅记载XML文档的相关方法. 关于XML文档: 1.一种便于编辑和传输的数据文件格式 2.xm ...

  9. [Swift通天遁地]七、数据与安全-(1)XML文档的创建和解析

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

随机推荐

  1. 计算机网络,HTTP - 头部中带X前缀的头部字段

    HTTP中,什么是"X-" Prefix header? 例如 github API 的response headers有很多X前缀的头部: 查一下MDN文档: Custom pr ...

  2. spring(五):AOP

    AOP(Aspect Oriented Programming) 面向切面编程,是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP). 在进行OOP开发时,都是基于对组件(比如 ...

  3. 「题解」「CF853B」Jury Meeting

    目录 题目 思路 代码 题目 传送门 思路 十分巧妙的差分前缀和好题. 题目板块完结之后,我看到有很多处理此题的方法,但总感觉差分前缀和比较巧妙. 首先,通过输入我们可以将每个人能在 \(0\) 号点 ...

  4. mysql 父子表 注意事项

    今天遇到一个问题,父子表关联查询时总是多出几条数据,后来排查是父子关系的字段 类型不一致导致的

  5. Python 多任务(线程) day2 (2)

    同步 1.概念 :同步就是协同步调,按预定的先后次序运行 互斥锁 当多个线程几乎同时修改某一共享数据的时候,需要运行同步控制,最简单的同步机制是引入互斥锁.某个线程要更改共享数据时,先将其锁定,此时资 ...

  6. 一文懂SSM项目中的web.xml常用配置项

    做web后端工程师,逃不过的web.xml,我们都知道配置这个文件是日常工作了,那么我们来把一些必须知道知识点梳理下. 我们把web项目启动的时候,首先加载的就是web.xml这个文件,只有这个文件所 ...

  7. Win10 系统运行VsCode出现白屏的问题(亲测有效)

    Win10 系统运行VsCode出现白屏的问题(亲测有效) 新买的本本,昨天VScode运行还正常,今天打开一直白屏,什么都没有,只有几个小格格,也不是卡死的那种,可以轻松关闭, 刚开始以为版本问题, ...

  8. 微信小程序 (全局配置和页面配置)

    全局配置 app.json 文件用来对微信小程序进行全局配置. 一.配置页面路径 二.window 配置全局默认的窗口 navigationBarTextStyle:导航栏的标题颜色 navigati ...

  9. 剑指offer 面试题38 字符串的排列

    我惯用的dfs模板直接拿来套 class Solution { public: vector<string> Permutation(string str) { if(str.empty( ...

  10. Linux shell cut 命令详解

    详细资料请参考:博客园骏马金龙 https://www.cnblogs.com/f-ck-need-u/p/7521357.html cut命令将行按指定的分隔符分割成多列,它的弱点在于不好处理多个分 ...