【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
JAVA解析XML手册
XML文档以层级标签的形式来组织数据,多用于配置文件、存储静态数据、交换数据。
XML文档解析
首先我们要知道,“XML中的内容都是结点”,这句话的意思是:XML文档中,无论是 <> </> 符号的里面的内容(属性)、之间的内容(结点值)、还是 <> 本身(结点),都是Node,就连标签之间的换行、空格都是一个节点。明白这个很重要,因为下面遍历结点时,返回的 属性名值对 、结点值内容、结点本身 都是一个Node类型。
实例
解析下面的XML文档
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <users> <user userId="1001" idcard="370826199001133290"> <name>张三</name> <address>广州</address> <age>18</age> </user> <user userId="1002" idcard="370826199001133291"> <name>李四</name> <address>大连</address> <age>19</age> </user> <user userId="1003" idcard="370826199001133292"> <name>王五</name> <address>广州</address> <age>20</age> </user> <user userId="1004" idcard="370826199001133293"> <age>21</age> <address>广州</address> <name>冯六</name> </user> </users>
DOM解析
DOM是html和xml的应用程序接口(API),以层次结构(类似于树型)来组织节点和信息片段,映射XML文档的结构,允许获取和操作文档的任意部分,是W3C的官方标准
原理
首先在内存中创建一个Document对象,然后把XML文档读取进来赋值给这个dom对象。由于dom对象是基于树结构的,所以对dom对象进行遍历即可。对内存中的dom对象可以进行查询、修改、删除操作,还可以写回原XML文档保存修改。
【优点】 ①允许应用程序对数据和结构做出更改。 ②访问是双向的,可以在任何时候在树中上下导航,获取和操作任意部分的数据。 【缺点】 ①整个文档必须一次性解析完。
②由于整个文档都需要载入内存,对于大文档成本高。
DOM解析详解
操作步骤:
1:创建DocumentBuilderFactory,由newInstance()方法获取工厂实例;
2:由工厂创建DocumentBuilder;
3:通过 Document dom=builder.parse(file); 读取xml文档创建dom对象;
4:通过dom对象的一系列方法获取某个结点、某名字的结点列表:
Element getElementById(String elementId) 返回具有带给定值的 ID 属性的 Element。 NodeList getElementsByTagName(String tagname) 按文档顺序返回包含在文档中且具有给定标记名称的所有 Element 的 NodeList。
5:通过NodeList对象获取结点列表中某个结点:
Node item(int index) 返回集合中的第 index 个项。
6:通过Node对象读取结点属性:
NamedNodeMap getAttributes() 包含此节点的属性的 NamedNodeMap(如果它是 Element);否则为 null。
然后对NamedNodeMap对象调用以下方法获取属性:(前面我们说过,属性也是结点)
Node getNamedItem(String name) 检索通过名称指定的节点。 或 Node item(int index) 返回映射中第 index 个项。
最后,对返回的属性通过以下方法获取名、值:(属性也是结点)
String getNodeName() 此节点的名称,取决于其类型; String getNodeValue() 此节点的值,取决于其类型;
7:获取结点的子节点列表:
NodeList getChildNodes() 包含此节点的所有子节点的 NodeList。
8:获取结点值:
String getNodeName() 此节点的名称,取决于其类型; String getNodeValue() 此节点的值,取决于其类型;
总结:DOM方法解析XML文档,把元素、元素属性、元素值都看作Node类型,通过node.getNodeName()获取元素名、属性名, 通过getNodeValue()获取属性值、元素值,通过getChildNodes()获取子节点们,通过item(i)获取第i个属性或者第i个子节点。
DOM解析代码实例
构造Document对象:
/** * 构建Document对象 * @param xmlPath * @return */ private static Document buildDocument(String xmlPath){ try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(xmlPath); return documentBuilder.parse(inputStream); } catch (Exception e) { System.out.println(e); } return null; }
遍历DOM对象
Document: XML文档对象,由解析器获取 NodeList: 节点数组 Node: 节点(包括element、#text) Element: 元素,可用于获取属性参数
DOM解析完整代码示例
package com.demo; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; public class DomDemo { public static void main(String[] args) { try { find(); } catch (Exception e) { System.out.println(e); } } private static User find() throws Exception { Document document = buildDocument("users.xml"); NodeList list = document.getElementsByTagName("user"); for (int i = 0; i < list.getLength(); i++) { Element element = (Element) list.item(i); User student = new User(); student.setUserId(Long.parseLong(element.getAttribute("userId"))); student.setIdCard(element.getAttribute("idcard")); student.setName(element.getElementsByTagName("name").item(0).getTextContent()); student.setAddress(element.getElementsByTagName("address").item(0).getTextContent()); student.setAge(element.getElementsByTagName("age").item(0).getTextContent()); System.out.println(student); } return null; } /** * 构建Document对象 * @param xmlPath * @return */ private static Document buildDocument(String xmlPath){ try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(xmlPath); return documentBuilder.parse(inputStream); } catch (Exception e) { System.out.println(e); } return null; } }
SAX(Simple API for XML)解析
流模型中的"推"模型分析方式。通过事件驱动,每发现一个节点就引发一个事件,事件推给事件处理器,通过回调方法完成解析工作,解析XML文档的逻辑需要应用程序完成
原理
通过parse(file,listener)函数用一个listener对xml文件进行查找,按顺序读取文档,遍历每个标签,当发现目标标签时,读取标签的属性、结点值等信息并返回。
【优势】 ①无需将整个xml文档载入内存,因此消耗内存少。 ②只在读取数据时检查数据,不需要保存在内存中。 ③可以在某个条件得到满足时停止解析,不必解析整个文档。 ④效率和性能较高,能解析大于系统内存的文档。 【缺点】 ①不能随机的访问xml中的节点。 ②单向导航,无法定位文档层次,很难同时访问同一文档的不同部分数据,不支持XPath。 ③不能修改文档。
SAX解析详解
操作步骤:
1:创建解析工厂:SAXParserFactory factory = SAXParserFactory.newInstance();
2:由工厂创建解析器:SAXParser parser = factory.newSAXParser();
3:通过解析器的parse()方法,对指定xml文档以指定handler之类进行解析查询:parser.parse(xmlFile, new MySaxListener());
我们要继承DefaultHandler类,定义相应的查询操作类:
1:重写父类中的文档开始方法、文档结束方法,定义开始、结束遍历xml文档时的操作:
void startDocument() 接收文档开始的通知。 void endDocument() 接收文档结束的通知。
2:重写父类的标签开始方法、标签结束方法,定义遍历到一个开始、结束标签时的操作:
void startElement(String uri, String localName, String qName, Attributes attributes) //参数qName是标签名、attributes是属性列表 接收元素开始的通知。 void endElement(String uri, String localName, String qName) 接收元素结束的通知。
3:重写**characters**(char[] ch, int start, int length)方法:
// 对事件发生时,元素的字符怎么处理 public void characters(char[] ch, int start, int length) throws SAXException { //参数ch是当上述4中事件随便一个发生时,对应的元素的值,值在ch中start开始,length长。从头到尾遍历整个xml文档时,每个标签的值依次被存入ch中。 }
总结:
也就是说,通过SAX解析xml文档是没有dom对象出现的,所以不会有node,不会有getNodeName()、getNodeValue()获取结点名、值。 总结:SAX解析XML文档的结点名是通过事件函数的参数qName获取的,属性是通过参数attributes的getValue("属性名")获取的, 结点值是通过当前事件函数发生时,characters(char[] ch, int start, int length)方法中的内容获取的。
【事件处理器类型】 ①访问XML DTD:DTDHandler ②低级访问解析错误:ErrorHandler ③访问文档内容:ContextHandler 【DefaultHandler类】 SAX事件处理程序的默认基类,实现了DTDHandler、ErrorHandler、ContextHandler和EntityResolver接口,通常 做法是,继承该基类,重写需要的方法,如startDocument() 注:关于遍历 ①深度优先遍历(Depthi-First Traserval) ②广度优先遍历(Width-First Traserval)
SAX解析详解
创建SAX解析器
SAXParserFactory saxf = SAXParserFactory.newInstance(); SAXParser sax = saxf.newSAXParser();
JDOM解析
JDOM方法是根据DOM方法的众多繁琐操作进行包装得到的,上面我们看到,DOM方法解析XML文档其实是很繁琐的,而且很混乱,标签、属性、换行空格都当作结点类型来处理。JDOM方法定义了一系列通俗、好记的方法来解析XML,方法的底层封装了一系列DOM操作,但是我们不必亲自去进行这些繁琐的工作了。
优点:
a、DOM方式的优点:查找方便,可以修改 缺点 a、DOM方式的缺点:装载整个文档,对内存容量要求高
在JDOM中,同一了根节点、普通结点、属性等全为Element类型。
JDOM解析详解
操作步骤:
1:创建一个SAXbuilder:SAXBuilder builder = new SAXBuilder();
2:创建文件输入流打开xml文件:InputStream in = new FileInputStream("XXX.xml");
3:通过builder,从输入流读取xml文件创建dom对象:Document dom = builder.build(in);
4:获取根节点:Element root=dom.getRootElement();
5:获取子节点列表:List<Element> childNodes = node.getChildren();
6:遍历子节点列表,获取第i个结点:Element node = childNodes.get(i);
7:读取结点信息:
1)结点属性值:node.getAttributeValue("属性名");
2)结点名:node.getName();
3)结点值:node.getValue();
4)子结点文本值:node.getChildText("子结点名");
Dom4j解析
Dom4j
是目前最流行、最好用的XML解析工具,解析XML的速度最快。
Dom4j
是一个Java的XML API,类似于jdom,用来读写XML文件的。
Mave依赖
<dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency>
DOM4j解析详解
操作步骤:
1:创建SAXReader:SAXReader reader = new SAXReader();
2:创建文件输入流打开xml文件:InputStream in = new FileInputStream("XXX.xml");
3:通过reader和输入流读取xml文件到内存创建Document对象:Document dom = reader.read(in);
4:获取根节点:Element root=dom.getRootElement();
5:获取子节点列表:List<Element> childNodes = root.elements();
6:遍历子节点:Element node = childNodes.get(i);
7:读取结点信息:
1)结点属性值:node.attributeValue("属性名");
2)结点名:node.getName();
3)结点值:node.getValue();
4)子结点文本值:node.elementText("子结点名")
实例代码
package com.demo; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.List; public class Dom4jDemo { public static void main(String[] args) throws DocumentException { SAXReader reader = new SAXReader(); InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("users.xml"); Document document = reader.read(inputStream); Element rootElement = document.getRootElement(); List<Element> elements = rootElement.elements("user"); for (Element element : elements) { User user = new User(); user.setUserId(Long.parseLong(element.attribute("userId").getValue())); user.setIdCard(element.attribute("idcard").getValue()); user.setName(element.elementText("name")); user.setAddress(element.elementText("address")); user.setAge(element.elementText("age")); System.out.println(user); } } }
总结:
1)DOM、JDOM、DOM4j都是把xml文档读取到内存中,生成dom对象进行遍历的;
DOM是Java原生的,所以比较繁琐;
JDOM是对DOM操作的封装,更加通俗、易记,操作也快了一点;
DOM4j解析xml的函数上与JDOM差不多,只不过有几个相同功能的函数名字不同而已,过程都是一样的;但由于底层使用了Xpath等方法加快了索引,所以检索性能更快。
2)SAX是基于事件驱动的,查询事件监听器继承自DefaultHandler,定义了检索xml过程中遇到开始标签、结束标签时执行的事件函数,从而查找需要的信息并返回而不是把整个文档都加载进来
比较
1)DOM4J性能最好,连Sun的JAXM也在用DOM4J。目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J.
2)JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出。在小文档情况下还值得考虑使用DOM和JDOM。虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。
3)SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存.还有就是只能读取不能修改xml(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。
来源:oschina
链接:https://my.oschina.net/u/1398304/blog/3153952