下面我将详细介绍这几种方法,并提供完整的代码示例。

场景设定
假设我们有以下这个 XML 字符串,我们要解析它并提取出 <user> 的信息:
<users>
<user id="101">
<name>张三</name>
<email>zhangsan@example.com</email>
<roles>
<role>admin</role>
<role>editor</role>
</roles>
</user>
<user id="102">
<name>李四</name>
<email>lisi@example.com</email>
<roles>
<role>user</role>
</roles>
</user>
</users>
DOM (Document Object Model) 解析
核心思想:将整个 XML 文档读入内存,构建一个树形结构,你可以随心所欲地遍历、查询、修改这棵树,最后还可以将修改写回文件。
优点:
- 非常直观,易于理解和使用。
- 可以在内存中自由导航和修改 XML 数据。
缺点:

- 内存消耗大:整个 XML 文档都会被加载到内存中,对于非常大的 XML 文件,这可能会导致
OutOfMemoryError。 - 速度相对较慢:因为需要完整解析和构建整个树。
适用场景:XML 文件较小,或者需要对 XML 进行多次读写和复杂操作的场景。
示例代码
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.ByteArrayInputStream;
public class DomParserExample {
public static void main(String[] args) {
String xmlString = "<users>\n" +
" <user id=\"101\">\n" +
" <name>张三</name>\n" +
" <email>zhangsan@example.com</email>\n" +
" <roles>\n" +
" <role>admin</role>\n" +
" <role>editor</role>\n" +
" </roles>\n" +
" </user>\n" +
" <user id=\"102\">\n" +
" <name>李四</name>\n" +
" <email>lisi@example.com</email>\n" +
" <roles>\n" +
" <role>user</role>\n" +
" </roles>\n" +
" </user>\n" +
"</users>";
try {
// 1. 创建 DocumentBuilderFactory 和 DocumentBuilder
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 2. 将 XML 字符串解析为 Document 对象
// 使用 ByteArrayInputStream 将字符串转换为 InputStream
Document document = builder.parse(new ByteArrayInputStream(xmlString.getBytes()));
// 3. 标准化文档,以便正确处理节点
document.getDocumentElement().normalize();
// 4. 获取所有 user 节点列表
NodeList nodeList = document.getElementsByTagName("user");
System.out.println("--- 使用 DOM 解析 XML ---");
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
// 获取 id 属性
String id = element.getAttribute("id");
// 获取子元素 "name" 的文本内容
String name = element.getElementsByTagName("name").item(0).getTextContent();
// 获取子元素 "email" 的文本内容
String email = element.getElementsByTagName("email").item(0).getTextContent();
System.out.println("用户 ID: " + id);
System.out.println(" 姓名: " + name);
System.out.println(" 邮箱: " + email);
// 解析列表 <roles>
NodeList roleNodeList = element.getElementsByTagName("role");
System.out.print(" 角色: [");
for (int j = 0; j < roleNodeList.getLength(); j++) {
System.out.print(roleNodeList.item(j).getTextContent());
if (j < roleNodeList.getLength() - 1) {
System.out.print(", ");
}
}
System.out.println("]\n");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
SAX (Simple API for XML) 解析
核心思想:事件驱动的解析方式,它不会将整个 XML 文档加载到内存,而是当解析器读到 XML 文档的某个部分(如开始标签、结束标签、文本内容等)时,会触发相应的事件(回调方法)。
优点:
- 内存效率极高:任何时候内存中只保留当前节点的信息,非常适合解析超大 XML 文件。
- 速度快:解析过程是顺序的,没有复杂的树结构构建。
缺点:

- 只能读,不能写:SAX 是只读的。
- 编程模型复杂:你需要自己维护状态,事件是顺序触发的,如果需要“回头看”之前的数据,会非常麻烦。
适用场景:解析非常大的 XML 文件,或者只需要对 XML 进行一次性的顺序读取。
示例代码
- 创建一个 Handler 类,继承
DefaultHandler并重写关键方法。
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.ByteArrayInputStream;
// 自定义的 Handler,用于处理解析事件
class UserHandler extends DefaultHandler {
private boolean inUser = false;
private boolean inName = false;
private boolean inEmail = false;
private boolean inRole = false;
private StringBuilder currentValue = new StringBuilder();
private User currentUser;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (qName) {
case "user":
inUser = true;
currentUser = new User();
currentUser.setId(attributes.getValue("id"));
break;
case "name":
inName = true;
break;
case "email":
inEmail = true;
break;
case "role":
inRole = true;
break;
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
switch (qName) {
case "user":
inUser = false;
System.out.println(currentUser);
break;
case "name":
inName = false;
currentUser.setName(currentValue.toString());
break;
case "email":
inEmail = false;
currentUser.setEmail(currentValue.toString());
break;
case "role":
inRole = false;
currentUser.addRole(currentValue.toString());
break;
}
currentValue.setLength(0); // 清空 StringBuilder
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
currentValue.append(ch, start, length);
}
}
// 一个简单的 User 类来存储数据
class User {
private String id;
private String name;
private String email;
private java.util.List<String> roles = new java.util.ArrayList<>();
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public java.util.List<String> getRoles() { return roles; }
public void addRole(String role) { this.roles.add(role); }
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", email='" + email + '\'' +
", roles=" + roles +
'}';
}
}
- 在主程序中使用 SAXParser
public class SaxParserExample {
public static void main(String[] args) {
String xmlString = "<users>\n" +
" <user id=\"101\">\n" +
" <name>张三</name>\n" +
" <email>zhangsan@example.com</email>\n" +
" <roles>\n" +
" <role>admin</role>\n" +
" <role>editor</role>\n" +
" </roles>\n" +
" </user>\n" +
" <user id=\"102\">\n" +
" <name>李四</name>\n" +
" <email>lisi@example.com</email>\n" +
" <roles>\n" +
" <role>user</role>\n" +
" </roles>\n" +
" </user>\n" +
"</users>";
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
// 创建自定义的 Handler
UserHandler handler = new UserHandler();
// 解析 XML 字符串
saxParser.parse(new ByteArrayInputStream(xmlString.getBytes()), handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}
StAX (Streaming API for XML) 解析
核心思想:结合了 DOM 和 SAX 的优点,它也是一种流式、事件驱动的 API,但它允许你主动“拉取” (pull) 事件,而不是像 SAX 那样被动地接收。
优点:
- 内存效率高:和 SAX 一样,是流式的,适合大文件。
- 编程模型更友好:因为是你主动拉取事件,所以更容易控制流程,代码比 SAX 更清晰。
- 速度较快。
缺点:
- 同样是只读的。
适用场景:与 SAX 类似,适合大文件解析,但当你觉得 SAX 的回调模型难以驾驭时,StAX 是一个很好的替代品。
示例代码
import javax.xml.stream.*;
import javax.xml.stream.events.*;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
public class StaxParserExample {
public static void main(String[] args) {
String xmlString = "<users>\n" +
" <user id=\"101\">\n" +
" <name>张三</name>\n" +
" <email>zhangsan@example.com</email>\n" +
" <roles>\n" +
" <role>admin</role>\n" +
" <role>editor</role>\n" +
" </roles>\n" +
" </user>\n" +
" <user id=\"102\">\n" +
" <name>李四</name>\n" +
" <email>lisi@example.com</email>\n" +
" <roles>\n" +
" <role>user</role>\n" +
" </roles>\n" +
" </user>\n" +
"</users>";
try {
// 1. 创建 XMLInputFactory
XMLInputFactory factory = XMLInputFactory.newInstance();
// 2. 创建 XMLEventReader
XMLEventReader eventReader = factory.createXMLEventReader(new ByteArrayInputStream(xmlString.getBytes()));
User currentUser = null;
List<String> roles = new ArrayList<>();
StringBuilder textContent = new StringBuilder();
// 3. 遍历事件
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
StartElement startElement = event.asStartElement();
String elementName = startElement.getName().getLocalPart();
if ("user".equals(elementName)) {
currentUser = new User();
currentUser.setId(startElement.getAttributeByName(new javax.xml.namespace.QName("id")).getValue());
roles.clear(); // 为新用户清空角色列表
} else if ("role".equals(elementName)) {
// 开始读取角色文本
}
}
if (event.isCharacters()) {
// 读取文本内容
textContent.append(event.asCharacters().getData());
}
if (event.isEndElement()) {
EndElement endElement = event.asEndElement();
String elementName = endElement.getName().getLocalPart();
if ("user".equals(elementName)) {
System.out.println(currentUser);
} else if ("name".equals(elementName)) {
currentUser.setName(textContent.toString());
} else if ("email".equals(elementName)) {
currentUser.setEmail(textContent.toString());
} else if ("role".equals(elementName)) {
roles.add(textContent.toString());
currentUser.setRoles(roles);
}
textContent.setLength(0); // 清空
}
}
eventReader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
(注意:这个 StAX 示例为了清晰,使用了 XMLEventReader,还有一个更简单的 XMLStreamReader,它提供更底层的“游标”式访问,性能更高。)
数据绑定 (推荐)
这是目前最流行、最高效的方法,你预先定义好与 XML 结构对应的 Java 类(POJO),然后使用 Jackson 或 Gson 这样的库,一行代码就能完成 XML 字符串到 Java 对象列表的转换。
优点:
- 代码极其简洁:几乎不需要写解析逻辑。
- 可读性和可维护性高:Java 对象的结构清晰,易于理解和使用。
- 性能优秀:底层通常是高效的流式解析。
缺点:
- 需要引入第三方库。
- 对于非常动态、结构不固定的 XML,可能需要更多配置。
示例代码 (使用 Jackson)
-
添加 Maven 依赖
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.15.2</version> <!-- 使用最新版本 --> </dependency> -
创建 Java 类
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import java.util.List;
// 根节点
@JacksonXmlRootElement(localName = "users")
public class UsersWrapper {
@JacksonXmlElementWrapper(useWrapping = false) // 不包装 <user> 标签
@JacksonXmlProperty(localName = "user") // 指定对应的 XML 标签名
private List<User> users;
// Getters and Setters
public List<User> getUsers() { return users; }
public void setUsers(List<User> users) { this.users = users; }
}
// User 类
public class User {
@JacksonXmlProperty(isAttribute = true, localName = "id") // 标记为属性
private String id;
@JacksonXmlProperty(localName = "name")
private String name;
@JacksonXmlProperty(localName = "email")
private String email;
@JacksonXmlProperty(localName = "role") // roles/role -> user.roles
private List<String> roles;
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public List<String> getRoles() { return roles; }
public void setRoles(List<String> roles) { this.roles = roles; }
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", email='" + email + '\'' +
", roles=" + roles +
'}';
}
}
- 主程序
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class JacksonXmlParserExample {
public static void main(String[] args) {
String xmlString = "<users>\n" +
" <user id=\"101\">\n" +
" <name>张三</name>\n" +
" <email>zhangsan@example.com</email>\n" +
" <role>admin</role>\n" +
" <role>editor</role>\n" +
" </user>\n" +
" <user id=\"102\">\n" +
" <name>李四</name>\n" +
" <email>lisi@example.com</email>\n" +
" <role>user</role>\n" +
" </user>\n" +
"</users>";
try {
XmlMapper xmlMapper = new XmlMapper();
// 一行代码完成解析!
UsersWrapper wrapper = xmlMapper.readValue(xmlString, UsersWrapper.class);
System.out.println("--- 使用 Jackson 解析 XML ---");
for (User user : wrapper.getUsers()) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结与如何选择
| 特性 | DOM | SAX | StAX | 数据绑定 (Jackson/Gson) |
|---|---|---|---|---|
| 内存占用 | 高 (整个树) | 低 (流式) | 低 (流式) | 低 (流式) |
| 速度 | 较慢 | 快 | 快 | 快 |
| 易用性 | 中等 | 复杂 (回调) | 中等 | 非常简单 |
| 可修改性 | 可读写 | 只读 | 只读 | 只读 (除非重新序列化) |
| 适用场景 | 小文件,复杂操作 | 超大文件,顺序读取 | 超大文件,需要更多控制 | 绝大多数现代应用,追求开发效率 |
选择建议:
- 新手或追求开发效率:直接使用 数据绑定 (Jackson/Gson),这是目前 Java 生态中最主流、最推荐的方式。
- 处理超大 XML 文件:如果内存是首要考虑因素,且 XML 结构非常固定,SAX 是经典选择,如果你觉得 SAX 编程模型难以忍受,StAX 是更好的现代替代品。
- 需要频繁修改 XML:如果解析后需要对 XML 结构进行增删改查,并且文件不大,DOM 是最直观的选择。
