XML作为跨平台的数据交换格式,在配置文件、接口数据传输等场景中应用广泛,掌握不同的XML解析方式能够帮助开发者更高效地应对各类开发需求。不同的解析方式在内存占用、访问灵活性、适用场景上各有差异,开发者可以根据实际需求选择合适的解析方案。

常见的XML解析方式分类
目前主流的XML解析方式可以分为三大类,分别是DOM解析、SAX解析和StAX解析,三者的核心差异如下:
| 解析方式 | 核心原理 | 内存占用 | 访问特性 | 适用场景 |
|---|---|---|---|---|
| DOM解析 | 将整个XML文档加载到内存,构建成树形节点结构 | 较高 | 支持随机访问、修改节点 | 小体积XML文件,需要频繁操作节点内容 |
| SAX解析 | 事件驱动的流式解析,逐行读取触发对应事件回调 | 很低 | 只能顺序读取,不支持修改 | 大体积XML文件,只需要读取数据不需要修改 |
| StAX解析 | 拉式流式解析,开发者主动调用方法获取下一个解析事件 | 很低 | 可控制解析进度,顺序读取 | 大体积XML文件,需要灵活控制解析流程 |
DOM解析实现代码
DOM解析需要先将整个XML文档加载到内存,然后通过节点操作API获取需要的内容,以下是解析一个简单用户配置XML的示例:
待解析的XML文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<name>张三</name>
<age>25</age>
<email>zhangsan@ipipp.com</email>
</user>
<user id="2">
<name>李四</name>
<age>28</age>
<email>lisi@ipipp.com</email>
</user>
</users>
对应的Java解析代码:
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
public class DOMParserDemo {
public static void main(String[] args) {
try {
// 创建DOM解析器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 创建DOM解析器
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析XML文件得到Document对象
Document document = builder.parse(new File("users.xml"));
// 获取所有user节点
NodeList userNodes = document.getElementsByTagName("user");
// 遍历user节点
for (int i = 0; i < userNodes.getLength(); i++) {
Node userNode = userNodes.item(i);
if (userNode.getNodeType() == Node.ELEMENT_NODE) {
Element userElement = (Element) userNode;
// 获取id属性
String id = userElement.getAttribute("id");
// 获取子节点name的内容
String name = userElement.getElementsByTagName("name").item(0).getTextContent();
// 获取子节点age的内容
String age = userElement.getElementsByTagName("age").item(0).getTextContent();
// 获取子节点email的内容
String email = userElement.getElementsByTagName("email").item(0).getTextContent();
System.out.println("用户ID:" + id + ",姓名:" + name + ",年龄:" + age + ",邮箱:" + email);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
SAX解析实现代码
SAX解析是事件驱动模式,需要自定义事件处理器,在解析过程中触发对应方法,以下是同样的XML文件的SAX解析示例:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
public class SAXParserDemo {
public static void main(String[] args) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
// 自定义事件处理器
DefaultHandler handler = new DefaultHandler() {
// 当前解析的user的id
private String currentUserId;
// 当前解析的标签名
private String currentTag;
// 当前解析的标签内容
private StringBuilder currentValue = new StringBuilder();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
currentTag = qName;
if ("user".equals(qName)) {
// 获取user节点的id属性
currentUserId = attributes.getValue("id");
}
// 重置内容缓存
currentValue.setLength(0);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
// 拼接标签内容
currentValue.append(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("name".equals(qName)) {
System.out.print("用户ID:" + currentUserId + ",姓名:" + currentValue.toString());
} else if ("age".equals(qName)) {
System.out.print(",年龄:" + currentValue.toString());
} else if ("email".equals(qName)) {
System.out.println(",邮箱:" + currentValue.toString());
}
}
};
saxParser.parse(new File("users.xml"), handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}
StAX解析实现代码
StAX解析是拉式解析,开发者可以主动控制解析的进度,通过循环获取下一个事件来处理,示例代码如下:
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.FileInputStream;
public class StAXParserDemo {
public static void main(String[] args) {
try {
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("users.xml"));
// 当前解析的user的id
String currentUserId = null;
// 当前解析的标签名
String currentTag = null;
// 当前解析的标签内容
StringBuilder currentValue = new StringBuilder();
// 循环获取下一个解析事件
while (reader.hasNext()) {
int event = reader.next();
switch (event) {
case XMLStreamReader.START_ELEMENT:
currentTag = reader.getLocalName();
if ("user".equals(currentTag)) {
// 获取user节点的id属性
currentUserId = reader.getAttributeValue(null, "id");
}
currentValue.setLength(0);
break;
case XMLStreamReader.CHARACTERS:
// 拼接标签内容,过滤空白字符
if (!reader.isWhiteSpace()) {
currentValue.append(reader.getText());
}
break;
case XMLStreamReader.END_ELEMENT:
String endTag = reader.getLocalName();
if ("name".equals(endTag)) {
System.out.print("用户ID:" + currentUserId + ",姓名:" + currentValue.toString());
} else if ("age".equals(endTag)) {
System.out.print(",年龄:" + currentValue.toString());
} else if ("email".equals(endTag)) {
System.out.println(",邮箱:" + currentValue.toString());
}
break;
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
解析方式选择建议
在实际开发中,选择XML解析方式可以参考以下原则:
- 如果XML文件体积较小,且需要频繁修改节点内容或者随机访问不同节点,优先选择DOM解析。
- 如果XML文件体积较大,只需要读取数据不需要修改,且不需要灵活控制解析进度,可以选择SAX解析。
- 如果XML文件体积较大,需要读取数据且希望主动控制解析流程,比如解析到某个条件后停止解析,优先选择StAX解析。
除了上述三种原生解析方式,实际开发中也可以使用第三方库比如JDOM、Dom4j等,这些库对原生API做了封装,使用起来更加简洁,但底层原理仍然基于上述三种解析方式。