Java中对XML的解析详解
XML(可扩展标记语言)是一种常用于存储和传输结构化数据的格式,在配置文件、数据交换等场景中应用广泛。Java生态中提供了多种XML解析方式,不同方式各有适用场景,本文将对常见的解析方案进行详细说明。
常见XML解析方式分类
Java中的XML解析方式主要分为两类:
- DOM解析:将整个XML文档加载到内存中,形成树形结构,允许随机访问和修改文档内容
- SAX解析:基于事件驱动的流式解析,逐行读取XML内容,不需要加载整个文档到内存,适合处理大体积XML
- StAX解析:同样基于流的方式,但由应用程序主动控制解析进度,相比SAX更灵活
- 第三方库解析:如JDOM、Dom4j等,对原生解析方式进行了封装,使用更便捷
DOM解析实战
DOM(Document Object Model)解析是Java原生支持的解析方式,核心思路是把XML文档转换为内存中的节点树,每个节点对应XML的元素、属性、文本等内容。这种方式的优点是支持对文档的增删改查,缺点是处理大文件时内存占用较高。
以下是一个简单的DOM解析示例,假设我们要解析的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>对应的DOM解析代码如下:
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
public class DomParseDemo {
public static void main(String[] args) {
try {
// 1. 创建DOM解析器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2. 创建DOM解析器
DocumentBuilder builder = factory.newDocumentBuilder();
// 3. 解析XML文件,得到Document对象(整个文档的树形结构)
Document document = builder.parse(new File("users.xml"));
// 4. 获取根节点 users
Element root = document.getDocumentElement();
System.out.println("根节点名称:" + root.getNodeName());
// 5. 获取所有user节点
NodeList userList = root.getElementsByTagName("user");
System.out.println("用户总数:" + userList.getLength());
// 6. 遍历每个user节点
for (int i = 0; i < userList.getLength(); i++) {
Node userNode = userList.item(i);
if (userNode.getNodeType() == Node.ELEMENT_NODE) {
Element userElement = (Element) userNode;
// 获取user节点的id属性
String id = userElement.getAttribute("id");
System.out.println("\n用户ID:" + 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("姓名:" + name);
System.out.println("年龄:" + age);
System.out.println("邮箱:" + email);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}上述代码首先通过DocumentBuilderFactory和DocumentBuilder创建解析器,再将XML文件解析为Document对象,之后通过节点操作方法获取需要的内容。如果需要修改XML内容,比如新增用户、修改年龄等,也可以直接操作Document对象,最后通过Transformer将修改后的内容写回文件。
SAX解析实战
SAX(Simple API for XML)是基于事件驱动的解析方式,解析器在读取XML的过程中,遇到开始标签、结束标签、文本内容等会触发对应的事件,应用程序通过实现事件处理器来响应这些事件,从而获取XML中的数据。这种方式不需要加载整个文档到内存,适合处理大体积XML,但不支持修改文档内容,也不支持随机访问。
以下是一个SAX解析的示例,解析上述同样的users.xml文件:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.*;
import java.io.File;
public class SaxParseDemo {
public static void main(String[] args) {
try {
// 1. 创建SAX解析器工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 2. 创建SAX解析器
SAXParser parser = factory.newSAXParser();
// 3. 创建事件处理器
DefaultHandler handler = new DefaultHandler() {
// 当前解析的用户ID
private String currentId;
// 当前解析的节点名称
private String currentTag;
// 用户姓名
private String name;
// 用户年龄
private String age;
// 用户邮箱
private String email;
// 遇到开始标签时触发
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
currentTag = qName;
if ("user".equals(qName)) {
// 获取user节点的id属性
currentId = attributes.getValue("id");
System.out.println("\n开始解析用户,ID:" + currentId);
}
}
// 遇到文本内容时触发
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String content = new String(ch, start, length).trim();
if (!content.isEmpty()) {
if ("name".equals(currentTag)) {
name = content;
} else if ("age".equals(currentTag)) {
age = content;
} else if ("email".equals(currentTag)) {
email = content;
}
}
}
// 遇到结束标签时触发
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("user".equals(qName)) {
// 一个user节点解析完成,输出内容
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
System.out.println("邮箱:" + email);
// 重置临时变量
name = null;
age = null;
email = null;
currentId = null;
}
currentTag = null;
}
};
// 4. 开始解析
parser.parse(new File("users.xml"), handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}上述代码通过继承DefaultHandler类实现自己的事件处理器,重写startElement、characters、endElement等方法,在解析过程中收集需要的数据。SAX解析的过程中,程序只能按顺序处理XML内容,无法回溯已经解析过的内容。
StAX解析实战
StAX(Streaming API for XML)是Java 6之后引入的解析方式,同样基于流,但不同于SAX的被动事件驱动,StAX由应用程序主动调用方法从解析器中获取下一个事件,控制更灵活。StAX分为游标API和迭代器API两种使用方式,以下示例使用更常用的游标API。
import javax.xml.stream.*;
import javax.xml.stream.events.XMLEvent;
import java.io.FileInputStream;
public class StaxParseDemo {
public static void main(String[] args) {
try {
// 1. 创建XML输入工厂
XMLInputFactory factory = XMLInputFactory.newInstance();
// 2. 创建XML事件阅读器
XMLEventReader reader = factory.createXMLEventReader(new FileInputStream("users.xml"));
// 临时变量
String currentId = null;
String name = null;
String age = null;
String email = null;
// 3. 循环获取下一个事件,直到文档结束
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
// 处理开始元素事件
if (event.isStartElement()) {
String tagName = event.asStartElement().getName().getLocalPart();
if ("user".equals(tagName)) {
// 获取user节点的id属性
currentId = event.asStartElement().getAttributeByName(new javax.xml.namespace.QName("id")).getValue();
System.out.println("\n开始解析用户,ID:" + currentId);
} else if ("name".equals(tagName)) {
// 获取name标签的文本内容
name = reader.nextEvent().asCharacters().getData();
} else if ("age".equals(tagName)) {
age = reader.nextEvent().asCharacters().getData();
} else if ("email".equals(tagName)) {
email = reader.nextEvent().asCharacters().getData();
}
}
// 处理结束元素事件
if (event.isEndElement()) {
if ("user".equals(event.asEndElement().getName().getLocalPart())) {
// 一个user节点解析完成,输出内容
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
System.out.println("邮箱:" + email);
// 重置临时变量
name = null;
age = null;
email = null;
currentId = null;
}
}
}
// 关闭资源
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}StAX的游标API使用起来比较直观,开发者可以灵活控制解析的进度,比如遇到某个条件时可以提前停止解析,不需要像SAX那样必须处理完整个文档。
第三方库Dom4j解析
Dom4j是一个开源的XML解析库,对DOM和SAX进行了封装,提供了更简洁的API,使用非常广泛。使用前需要导入Dom4j的依赖,如果是Maven项目,可以在pom.xml中添加以下依赖:
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>以下是使用Dom4j解析users.xml的示例:
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
public class Dom4jParseDemo {
public static void main(String[] args) {
try {
// 1. 创建SAXReader对象
SAXReader reader = new SAXReader();
// 2. 读取XML文件,得到Document对象
Document document = reader.read(new File("users.xml"));
// 3. 获取根节点
Element root = document.getRootElement();
System.out.println("根节点名称:" + root.getName());
// 4. 获取所有user子节点
List<Element> userElements = root.elements("user");
System.out.println("用户总数:" + userElements.size());
// 5. 遍历user节点
for (Element userElement : userElements) {
// 获取id属性
String id = userElement.attributeValue("id");
System.out.println("\n用户ID:" + id);
// 获取name子节点内容
String name = userElement.elementText("name");
// 获取age子节点内容
String age = userElement.elementText("age");
// 获取email子节点内容
String email = userElement.elementText("email");
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
System.out.println("邮箱:" + email);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}Dom4j的API非常简洁,比如获取子节点内容直接使用elementText方法,不需要像原生DOM那样先获取节点列表再取内容,大大降低了编码复杂度,同时它也支持对XML文档的修改和创建。如果需要创建新的XML文档,Dom4j也提供了非常便捷的API,比如通过DocumentHelper.createElement创建节点,再组装成完整的文档结构。
不同解析方式的选型建议
| 解析方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| DOM解析 | 支持随机访问、增删改查,API直观 | 内存占用高,不适合大文件 | 小体积XML,需要修改文档内容的场景 |
| SAX解析 | 内存占用低,解析速度快 | 不支持随机访问和内容修改,编码相对复杂 | 大体积XML,只需要读取内容不需要修改的场景 |
| StAX解析 | 内存占用低,应用程序可控制解析进度 | API相对DOM复杂一些 | 大体积XML,需要灵活控制解析过程的场景 |
| Dom4j | API简洁易用,功能全面 | 需要引入第三方依赖 | 绝大多数XML解析场景,尤其是需要快速开发的情况 |
在实际开发中,如果没有特殊的内存限制,优先选择Dom4j等第三方库可以大幅提升开发效率;如果是Android开发等对环境依赖要求严格的场景,可以考虑使用原生的DOM或StAX解析。
Java_XML解析DOM解析SAX解析StAX解析Dom4j 本作品最后修改时间:2026-05-22 21:45:03