目录
1、JDK提供的两种XML原生解析方式
参考文档:Java解析XML(4种方法)
1.1 Dom
Dom解析XML的过程是一次将整个xml文档全部加载到内存中,然后进行解析与访问。
优点:
1、XML形成了树数据结构,直观比较容易理解,编码比较容易
2、因为XML以树的形式保留在内存中,方便进行修改
缺点:
1、不太适合解析大XML文件,当解析大XML文件时,对内存耗费比较大,容易造成性能问题,造成内存溢出。
下面给出用JDK DOM方式解析一个XML文件的示例:
<?xml version="1.0" encoding="utf-8" ?>
<students>
<student stuNo="1">
<name>张三</name>
<nickname>张三</nickname>
<score>85</score>
</student>
<student stuNo="2">
<name>李四</name>
<nickname>李四</nickname>
<score>85</score>
</student>
<student stuNo="3">
<name>王五</name>
<nickname>王五</nickname>
<score>85</score>
</student>
</students>
package com.autocoding.xml;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
*
* @Description: JDK原生:Dom 方式解析XML
* @author: QiaoLi
* @date: Jan 25, 2021 9:55:14 AM
*/
public class DomDemo {
public static void node(NodeList list) {
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
NodeList childNodes = node.getChildNodes();
for (int j = 0; j < childNodes.getLength(); j++) {
if (childNodes.item(j).getNodeType() == Node.ELEMENT_NODE) {
System.out.print(childNodes.item(j).getNodeName() + ":");
System.out.println(childNodes.item(j).getFirstChild().getNodeValue());
}
}
System.out.println();
}
}
public static void main(String[] args) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("src/test/resources/students.xml");
NodeList nodeList = document.getElementsByTagName("student");
DomDemo.node(nodeList);
} catch (Exception e) {
e.printStackTrace();
}
}
}
程序运行结果:
1.1.1 Mybatis与Dom
Mybatis是采用Dom方式来解析XML configuration配置文件的。org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XNode) 核心方法:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
XNode代码如下:
* Copyright 2009-2019 the original author or authors.
package org.apache.ibatis.parsing;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author Clinton Begin
*/
public class XNode {
private final Node node;
private final String name;
private final String body;
private final Properties attributes;
private final Properties variables;
private final XPathParser xpathParser;
public XNode(XPathParser xpathParser, Node node, Properties variables) {
this.xpathParser = xpathParser;
this.node = node;
this.name = node.getNodeName();
this.variables = variables;
this.attributes = parseAttributes(node);
this.body = parseBody(node);
}
...... 代码省略
}
1.2 SAX
SAX采用事件驱动模式解析XML文件,访问一个Element发一个事件,对应的DefaultHandler就会进行事件处理。
优点:
1、采用事件驱动模式,对内存消耗比较小,不会有内存溢出问题
2、只适合读取XML文件,整个文档结构在内存中并产保存,后期无法修改XML文档
缺点:
1、需要开发自己的DefaultHandler事件处理器,编码相对于Dom来有一定的门槛
下面给出用JDK SAX 方式解析一个XML文件的示例,XML文件也上述中的students.xml相同 :
package com.autocoding.xml;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SaxDemo {
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
MyHandler handler = new MyHandler();
saxParser.parse("src/test/resources/students.xml", handler);
}
private static class MyHandler extends DefaultHandler {
// 遍历xml文件开始标签
@Override
public void startDocument() throws SAXException {
super.startDocument();
System.out.println("sax解析开始");
}
// 遍历xml文件结束标签
@Override
public void endDocument() throws SAXException {
super.endDocument();
System.out.println("sax解析结束");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
super.startElement(uri, localName, qName, attributes);
if (qName.equals("student")) {
System.err.println("============开始遍历student=============");
} else if (!qName.equals("student") && !qName.equals("students")) {
log.info("uri:{},localName:{},qName:{}", uri, localName, qName);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
if (qName.equals("student")) {
log.info("uri:{},localName:{},qName:{}", uri, localName, qName);
System.err.println("============结束遍历student=============");
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
String value = new String(ch, start, length).trim();
if (!value.equals("")) {
System.out.println(value);
}
}
}
}
下面是程序运行结果:
sax解析开始
============开始遍历student=============
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.startElement(SaxDemo.java:44) com.autocoding.xml.SaxDemo
uri:,localName:,qName:name
张三
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.startElement(SaxDemo.java:44) com.autocoding.xml.SaxDemo
uri:,localName:,qName:nickname
张三
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.startElement(SaxDemo.java:44) com.autocoding.xml.SaxDemo
uri:,localName:,qName:score
85
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.endElement(SaxDemo.java:52) com.autocoding.xml.SaxDemo
uri:,localName:,qName:student
============结束遍历student=============
============开始遍历student=============
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.startElement(SaxDemo.java:44) com.autocoding.xml.SaxDemo
uri:,localName:,qName:name
李四
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.startElement(SaxDemo.java:44) com.autocoding.xml.SaxDemo
uri:,localName:,qName:nickname
李四
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.startElement(SaxDemo.java:44) com.autocoding.xml.SaxDemo
uri:,localName:,qName:score
85
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.endElement(SaxDemo.java:52) com.autocoding.xml.SaxDemo
uri:,localName:,qName:student
============结束遍历student=============
============开始遍历student=============
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.startElement(SaxDemo.java:44) com.autocoding.xml.SaxDemo
uri:,localName:,qName:name
王五
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.startElement(SaxDemo.java:44) com.autocoding.xml.SaxDemo
uri:,localName:,qName:nickname
王五
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.startElement(SaxDemo.java:44) com.autocoding.xml.SaxDemo
uri:,localName:,qName:score
85
2021-01-25 10:17:09 [INFO] com.autocoding.xml.SaxDemo$MyHandler.endElement(SaxDemo.java:52) com.autocoding.xml.SaxDemo
uri:,localName:,qName:student
============结束遍历student=============
sax解析结束
2、Jdom/Dom4j
2.1 Jdom
为了快速开发XML应用程序,同时为了弥补JDK原生两种DOM与SAX方式在应用中的不足,Jdom出现了,它是一个开源项目。Jdom仅使用具体类而没有面向抽象编程,Jdom
API中大量使用了Collection集合框架中的类。
2.2 Dom4j
Dom4j是Jdom的一个分支,它合并了许多超出基本XML文档的功能,并且Dom4j面向抽象进行编程,它大量使用了接口与抽象类,是一个优秀的Java XML API;
它具有性能优异、灵活性好、功能强大、易于使用的优点;开源。
下面是一个示例代码,解析 students.xml 文件:
package com.autocoding.xml;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* Dom4J 解析XML
* @author: QiaoLi
* @date: Jan 25, 2021 10:36:23 AM
*/
public class Dom4JDemo {
public static void main(String[] args) throws Exception {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("src/test/resources/students.xml"));
Element rootElement = document.getRootElement();
Iterator<Element> iterator = rootElement.elementIterator();
while (iterator.hasNext()) {
Element stu = iterator.next();
List<Attribute> attributes = stu.attributes();
for (Attribute attribute : attributes) {
System.err.println(attribute.getName()+":"+attribute.getValue());
}
System.out.println("======遍历子节点======");
Iterator<Element> stuChildren = stu.elementIterator();
while (stuChildren.hasNext()) {
Element stuChild = stuChildren.next();
System.out.println("节点名:" + stuChild.getName() + "---节点值:" + stuChild.getStringValue());
}
System.out.println();
}
}
}
下面是程序运行结果:
2.2.1 Spring与Dom4J
spring解析xml配置的第三方库需要的是dom4j。参考文档:spring的xml解析原理
3、OXMapping技术
使 用 XML API 解析是略显烦琐的,受 ORMapping 技术的启发,人们发明了 OXMapping 技术,使用 OXMapping 技术,我们可以将 XML 文件映射成一个 JavaBean 对象,也可以把一个 JavaBean 对象保存成一个 XML 文件,
这大大简化了我们的开发工作量,使得开发人员能更多的关注应用层面的东西。开源世界中涌现出很多 OXMapping 框架,包括 XStream 、 Digester 、 Castor 等。 XStream 和 Digester 把映射的过程在代码中完成,
而 Castor 则需要写一个和 Hibernate 中 cfg.xml 类似的映射配置文件。与 Digester 比起来, XStream 的主要优点就是更加小巧,使用也更加方便,不过目前使用 Digester 是“开源名牌” Apache 下的子项目,
网上可以参考的资料也比 XStream 多,好在 XStream 比较简洁,所以并不会对 XStream 造成太大影响。
目前OXMaaping的一个流行的实现是XStream,它即是OXMapping的实现,同时实现了序列化与反序列化,可以很方便地对Java Object 与 Xml之间进行转换。
XStream是Java类库,用来将对象序列化成XML (JSON)或反序列化为对象。也就是说,使用XStream,我们可以把Java对象转换成XML,也可以将XML转换为Java对象。
参考文档:XStream 简介