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

时间:2023-01-07 17:58:29

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(兄弟)

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

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 子接口

JavaEE实战——XML文档DOM、SAX、STAX解析方式详解、Node对象提供了一系列常量来代表节点的类型(查看org.w3c.dom.Node接口源码):

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

当开发人员获得某个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总结:

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

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解析器 ---- 解析器调用处理器相应的事件处理方法来处理文档

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

为什么说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 解析方式 ---- 拉模式解析

JavaEE实战——XML文档DOM、SAX、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操作原理:

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

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

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

将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 
阅读(...) 
评论(...) 
编辑 
收藏