一、XML常见解析方式
常见解析XML的方法主要有DOM和SAX
1.DOM解析方式-基于文档树
DOM,即文档对象模型(Document Object Model),将XML文档解析成树状模型并将其放入内存来完成解析工作的,而后对文档的操作都是在这个树状模型上完成的。这个在内存中的文档树将是文档实际大小的几倍。
2.SAX解析方式-事件驱动
即XML简单应用程序接口-Simple API for XML,通读整个文档,根据文档内容产生事件,而把对这些事件的处理交由事件处理器处理。
3.DOM与SAX解析方式的对比
SAX | DOM |
顺序读入文档并产生相应事件,可以处理任何大小的XML文档 | 在内存中创建文档树,不适于处理大型XML文档。 |
只能对文档按顺序解析一遍,不支持对文档的随意访问。 | 可以随意访问文档树的任何部分,没有次数限制。 |
只能读取XML文档内容,而不能修改 | 可以随意修改文档树,从而修改XML文档。 |
开发上比较复杂,需要自己来实现事件处理器。 | 易于理解,易于开发。 |
对开发人员而言更灵活,可以用SAX创建自己的XML对象模型。 | 已经在DOM基础之上创建好了文档树。 |
二、Java中解析XML
Sun公司提供了 java API for XML Parsing(JAXP)接口来使用SAX和DOM,通过JAXP,我们可以使用任何与JAXP兼容的XML解析器。
1.基础类与待解析XML
<?xml version="1.0" encoding="UTF-8"?> <MemInfo class="0501"> <person no="1"> <name>James</name> <age>32</age> </person> <person no="2"> <name>Kim</name> <age>38</age> </person> <person no="3"> <name>Joe</name> <age>24</age> </person> </MemInfo>
public class ClassInfo {
private String no;
private List<Person> students;
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public List<Person> getStudents() {
return students;
}
public void setStudents(List<Person> students) {
this.students = students;
}
}
public class Person {
private String no;
private String name;
private byte age;
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public byte getAge() {
return age;
}
public void setAge(byte age) {
this.age = age;
}
}
2.SAX方式解析
import java.util.ArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.alibaba.fastjson.JSONObject;
/**
* SAX解析器
*/
public class MemInfoParser extends DefaultHandler {
/**
* log4j日志
*/
protected static Logger log = LogManager.getLogger();
private ClassInfo cls;
private Person person;
/**
*
*/
private String preTag;
/**
* 文档开始调用
*/
@Override
public void startDocument() throws SAXException {
cls = new ClassInfo();
cls.setStudents(new ArrayList<>());
}
/**
* 文档结束调用
*/
@Override
public void endDocument() throws SAXException {
log.info("解析获得数据:" + JSONObject.toJSONString(cls));
}
/**
* 元素处理开始调用-多次
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (qName) {
case "MemInfo":
cls.setNo(attributes.getValue("class"));
break;
case "person":
person = new Person();
person.setNo(attributes.getValue("no"));
break;
default:
break;
}
preTag = qName;
}
/**
* 元素处理结束调用-多次
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
switch (qName) {
case "MemInfo":
break;
case "person":
cls.getStudents().add(person);
person = null;
break;
default:
break;
}
preTag = null;
}
/**
* 处理TextNode文本节点调用-多次
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//preTag为空,表示处理的是空白的文本节点,丢弃掉,PS:元素之间的空白部分SAX解析器会作为文本节点处理,如person、name节点间的空白
if (preTag == null)
return;
// 文本内容
String text = new String(ch, start, length);
switch (preTag) {
case "name":
person.setName(text);
break;
case "age":
person.setAge(Byte.parseByte(text));
break;
default:
break;
}
}
}
测试类:
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.XMLReader;
public class SaxParserTest {
public static void main(String[] args) throws Exception {
String path = "/data/workspace/tec-demo/src/main/java/cn/tinyf/demo/xml/sax/MemInfo.xml";
// 创建解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 创建解析器
SAXParser parser = factory.newSAXParser();
// 得到读取器
XMLReader reader = parser.getXMLReader();
// 设置内容处理器
MemInfoParser handler = new MemInfoParser();
reader.setContentHandler(handler);
// 读取xml文档
reader.parse(path);
}
}
3.DOM方式读写
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.alibaba.fastjson.JSONObject;
/**
* XML解析-Dom实现
*/
public class MemInfoParser {
public static void main(String[] args) {
String path = "/data/workspace/tec-demo/src/main/java/cn/tinyf/demo/xml/MemInfo.xml";
System.out.println(JSONObject.toJSONString(parser(path)));
}
public static ClassInfo parser(String docPath) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 从 DocumentBuilderFactory获取 DocumentBuilder实例
DocumentBuilder db;
try {
// 从 XML 文档获取 DOM 文档实例
db = dbf.newDocumentBuilder();
Document doc = db.parse(new File(docPath));
/*
* 创建相关对象存储XML数据
*/
ClassInfo cls = new ClassInfo();
List<Person> stuList = new ArrayList<>();
cls.setStudents(stuList);
// 获取文档节点中的班级信息
cls.setNo(doc.getDocumentElement().getAttribute("class"));
/*
* 获取所有学生节点,并遍历获取数据
*/
NodeList stuNodes = doc.getElementsByTagName("person");
int len = stuNodes.getLength();
for (int i = 0; i < len; i++) {
Element stu = (Element) stuNodes.item(i);
Node eltName = stu.getElementsByTagName("name").item(0);
Node eltAge = stu.getElementsByTagName("age").item(0);
Person person = new Person();
person.setName(eltName.getFirstChild().getNodeValue());
person.setNo(stu.getAttribute("no"));
person.setAge(Byte.parseByte(eltAge.getFirstChild().getNodeValue()));
stuList.add(person);
}
return cls;
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
/**
* XML生成-dom方式
*/
public class MemInfoBuilder {
/**
* log4j2日志
*/
protected static Logger log = LogManager.getLogger();
public static void main(String[] args) {
String xmlPath = "/data/workspace/tec-demo/src/main/java/cn/tinyf/demo/xml/dom/dom-data.xml";
//
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 从 DocumentBuilderFactory获取 DocumentBuilder实例
DocumentBuilder db;
try {
// 从 XML 文档获取 DOM 文档实例
db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
/*
* 生成文档树
*/
// 根节点
Element root = doc.createElement("MemInfo");
// 设置根节点属性
root.setAttribute("class", "0501");
// 为根节点添加子节点数据
root.appendChild(createStuElement(doc, "1", "James", 32));
root.appendChild(createStuElement(doc, "2", "Kim", 38));
root.appendChild(createStuElement(doc, "3", "Joe", 24));
// 根节点添加到文档树
doc.appendChild(root);
/*
* 准备生成文件
*/
// 设置XML声明中standalone为yes,即没有dtd和schema作为该XML的说明文档,且不显示该属性
doc.setXmlStandalone(true);
// 创建TransformerFactory对象
TransformerFactory tff = TransformerFactory.newInstance();
// 创建Transformer对象
Transformer tf = tff.newTransformer();
//
tf.setOutputProperty(OutputKeys.INDENT, "yes");
// 输出到文件
tf.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(xmlPath)));
} catch (ParserConfigurationException | FileNotFoundException | TransformerException e) {
log.error(e);
}
}
private static Element createStuElement(Document doc, String no, String name, int age) {
Element stuElem = doc.createElement("person");
stuElem.setAttribute("no", no);
//创建姓名节点
Element nameElem = doc.createElement("name");
nameElem.appendChild(doc.createTextNode(name));
//创建年龄节点
Element ageElem = doc.createElement("age");
ageElem.appendChild(doc.createTextNode(age + ""));
//将姓名、年龄节点添加到学生节点中并返回
stuElem.appendChild(nameElem);
stuElem.appendChild(ageElem);
return stuElem;
}
}
三、其他解析器
1. JDOM
JDOM是一个开源项目,它基于树型结构,利用纯JAVA的技术对XML文档实现解析、生成、序列化以及多种操作。
Jdom可以和已有的XML技术如Simple API for XML (SAX)和 Document Object Model (DOM)相互协作。
2.dom4j
dom4j是一个开源的Java的XML API,是jdom的升级品,用来读写XML文件的。dom4j是一个十分优秀的JavaXML API,具有性能优异、功能强大和极其易使用的特点,它的性能超过sun公司官方的dom技术。