- DOM (Document Object Model): 将整个 XML 文档一次性加载到内存中,形成一个树形结构,优点是易于遍历和修改,缺点是如果 XML 文件很大,会消耗大量内存。
- SAX (Simple API for XML): 一种事件驱动的解析模型,它不会加载整个文档,而是从上到下逐行读取,当遇到某个元素(如开始标签、结束标签、文本内容)时,会触发相应的事件,优点是内存占用小,适合处理大文件,缺点是只能读取,不能修改,且编程模型相对复杂。
下面我将详细介绍如何使用这两种主流方法,以及一个更现代、更简洁的替代方案 StAX。

准备工作:示例 XML 字符串
我们将使用以下 XML 字符串作为所有示例的输入:
<?xml version="1.0" encoding="UTF-8"?>
<library>
<book category="FICTION">
<title lang="en">The Great Gatsby</title>
<author>F. Scott Fitzgerald</author>
<year>1925</year>
<price currency="USD">15.99</price>
</book>
<book category="SCIENCE">
<title lang="en">A Brief History of Time</title>
<author>Stephen Hawking</author>
<year>1988</year>
<price currency="GBP">10.50</price>
</book>
</library>
使用 DOM 解析
DOM 解析器会将 XML 字符串解析成一个 Document 对象,然后你可以像操作一棵树一样操作它。
核心类
DocumentBuilderFactory: 创建DocumentBuilder的工厂。DocumentBuilder: 负责将 XML 源(如文件、输入流、字符串)解析为Document对象。Document: 代表整个 XML 文档的内存中的树形结构。Node:Document,Element,Text等所有节点的基类。Element: 代表 XML 中的元素(如<book>,<title>)。NodeList: 节点列表,通常用于获取某个元素下的所有子元素。
完整代码示例
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
public class DomXmlParser {
public static void main(String[] args) {
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<library>\n" +
" <book category=\"FICTION\">\n" +
" <title lang=\"en\">The Great Gatsby</title>\n" +
" <author>F. Scott Fitzgerald</author>\n" +
" <year>1925</year>\n" +
" <price currency=\"USD\">15.99</price>\n" +
" </book>\n" +
" <book category=\"SCIENCE\">\n" +
" <title lang=\"en\">A Brief History of Time</title>\n" +
" <author>Stephen Hawking</author>\n" +
" <year>1988</year>\n" +
" <price currency=\"GBP\">10.50</price>\n" +
" </book>\n" +
"</library>";
try {
// 1. 创建 DocumentBuilderFactory 和 DocumentBuilder
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 2. 将 XML 字符串转换为输入流并解析
ByteArrayInputStream inputStream = new ByteArrayInputStream(xmlString.getBytes());
Document document = builder.parse(inputStream);
// 3. 标准化文档结构(可选,但推荐)
document.getDocumentElement().normalize();
// 4. 获取根元素 <library>
Element root = document.getDocumentElement();
System.out.println("Root element: " + root.getNodeName());
// 5. 获取所有 <book> 节点
NodeList bookList = root.getElementsByTagName("book");
// 6. 遍历所有 <book> 节点
for (int i = 0; i < bookList.getLength(); i++) {
Node bookNode = bookList.item(i);
if (bookNode.getNodeType() == Node.ELEMENT_NODE) {
Element bookElement = (Element) bookNode;
// 获取元素的属性
String category = bookElement.getAttribute("category");
System.out.println("\nBook Category: " + category);
// 获取 <title>, <author>, <year> 等子元素
String title = getTagValue("title", bookElement);
String author = getTagValue("author", bookElement);
String year = getTagValue("year", bookElement);
String price = getTagValue("price", bookElement);
String currency = bookElement.getElementsByTagName("price").item(0).getAttributes().getNamedItem("currency").getNodeValue();
System.out.println(" Title: " + title);
System.out.println(" Author: " + author);
System.out.println(" Year: " + year);
System.out.println(" Price: " + price + " (" + currency + ")");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 辅助方法:获取标签的文本内容
private static String getTagValue(String tag, Element element) {
NodeList nodeList = element.getElementsByTagName(tag).item(0).getChildNodes();
Node node = nodeList.item(0);
return node.getNodeValue();
}
}
优点与缺点
- 优点:
- 编程模型直观,易于理解和使用。
- 可以方便地在内存中遍历、查询、修改和创建 XML 结构。
- 缺点:
- 内存消耗大:将整个 XML 文档加载到内存中,不适合处理大型 XML 文件(几百MB或GB级别)。
- 性能较低:加载整个文档需要时间,对于只读操作来说开销较大。
使用 SAX 解析
SAX 解析器是事件驱动的,你需要定义一个 Handler 来处理不同的事件(如开始元素、结束元素、遇到文本等)。
核心类
SAXParserFactory: 创建SAXParser的工厂。SAXParser: 执行实际的解析工作。org.xml.sax.helpers.DefaultHandler: 一个方便的基类,你可以继承它并重写特定方法来处理事件。startElement(): 遇到开始标签时触发。endElement(): 遇到结束标签时触发。characters(): 遇到标签内的文本内容时触发。
完整代码示例
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;
import java.io.IOException;
public class SaxXmlParser {
public static void main(String[] args) {
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<library>\n" +
" <book category=\"FICTION\">\n" +
" <title lang=\"en\">The Great Gatsby</title>\n" +
" <author>F. Scott Fitzgerald</author>\n" +
" <year>1925</year>\n" +
" <price currency=\"USD\">15.99</price>\n" +
" </book>\n" +
" <book category=\"SCIENCE\">\n" +
" <title lang=\"en\">A Brief History of Time</title>\n" +
" <author>Stephen Hawking</author>\n" +
" <year>1988</year>\n" +
" <price currency=\"GBP\">10.50</price>\n" +
" </book>\n" +
"</library>";
try {
// 1. 创建 SAXParserFactory 和 SAXParser
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
// 2. 创建自定义的 Handler
DefaultHandler handler = new DefaultHandler() {
boolean inBook = false;
boolean inTitle = false;
boolean inAuthor = false;
boolean inYear = false;
boolean inPrice = false;
String currentCategory;
String currentCurrency;
// 遇到开始标签时触发
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("book")) {
inBook = true;
currentCategory = attributes.getValue("category");
System.out.println("\nBook Category: " + currentCategory);
}
if (qName.equalsIgnoreCase("title")) {
inTitle = true;
}
if (qName.equalsIgnoreCase("author")) {
inAuthor = true;
}
if (qName.equalsIgnoreCase("year")) {
inYear = true;
}
if (qName.equalsIgnoreCase("price")) {
inPrice = true;
currentCurrency = attributes.getValue("currency");
}
}
// 遇到结束标签时触发
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equalsIgnoreCase("book")) {
inBook = false;
}
if (qName.equalsIgnoreCase("title")) {
inTitle = false;
}
if (qName.equalsIgnoreCase("author")) {
inAuthor = false;
}
if (qName.equalsIgnoreCase("year")) {
inYear = false;
}
if (qName.equalsIgnoreCase("price")) {
inPrice = false;
}
}
// 遇到文本内容时触发
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String value = new String(ch, start, length).trim();
if (value.isEmpty()) {
return; // 忽略纯空白字符
}
if (inTitle) {
System.out.println(" Title: " + value);
}
if (inAuthor) {
System.out.println(" Author: " + value);
}
if (inYear) {
System.out.println(" Year: " + value);
}
if (inPrice) {
System.out.println(" Price: " + value + " (" + currentCurrency + ")");
}
}
};
// 3. 执行解析
saxParser.parse(new ByteArrayInputStream(xmlString.getBytes()), handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}
优点与缺点
- 优点:
- 内存效率高:任何时候内存中只保留当前节点和路径信息,非常适合解析超大 XML 文件。
- 速度快:因为不需要构建整个树,解析速度通常比 DOM 快。
- 缺点:
- 只读:SAX 解析器本身不支持修改 XML。
- 编程复杂:事件驱动的模型不如 DOM 直观,处理复杂的嵌套逻辑和混合内容(文本和元素混合)比较麻烦。
- 随机访问困难:不能随意跳转到文档的任意部分,只能从头开始顺序解析。
使用 StAX 解析 (推荐)
StAX (Streaming API for XML) 是一种“拉” (pull) 模型的 API,介于 DOM 和 SAX 之间,它让你主动地从解析器中“拉”出事件,而不是被动地接收 SAX 的事件,它比 SAX 更灵活,比 DOM 更节省内存。

核心类
javax.xml.stream.XMLInputFactory: 创建XMLEventReader的工厂。javax.xml.stream.XMLEventReader: 读取 XML 事件的游标。javax.xml.stream.events.XMLEvent: 代表一个 XML 事件(如开始元素、结束元素、字符等)。javax.xml.stream.util.StreamReaderDelegate: (可选) 用于包装XMLEventReader以增加功能。
完整代码示例
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Iterator;
public class StaxXmlParser {
public static void main(String[] args) {
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<library>\n" +
" <book category=\"FICTION\">\n" +
" <title lang=\"en\">The Great Gatsby</title>\n" +
" <author>F. Scott Fitzgerald</author>\n" +
" <year>1925</year>\n" +
" <price currency=\"USD\">15.99</price>\n" +
" </book>\n" +
" <book category=\"SCIENCE\">\n" +
" <title lang=\"en\">A Brief History of Time</title>\n" +
" <author>Stephen Hawking</author>\n" +
" <year>1988</year>\n" +
" <price currency=\"GBP\">10.50</price>\n" +
" </book>\n" +
"</library>";
try {
// 1. 创建 XMLInputFactory 和 XMLEventReader
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLEventReader eventReader = factory.createXMLEventReader(new ByteArrayInputStream(xmlString.getBytes()));
String currentElement = null;
String currentCategory = null;
String currentCurrency = null;
// 2. 遍历所有事件
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
// 3. 判断事件类型
if (event.isStartElement()) {
StartElement startElement = event.asStartElement();
currentElement = startElement.getName().getLocalPart();
if (currentElement.equals("book")) {
currentCategory = startElement.getAttributeByName(new javax.xml.namespace.QName("category")).getValue();
System.out.println("\nBook Category: " + currentCategory);
}
if (currentElement.equals("price")) {
currentCurrency = startElement.getAttributeByName(new javax.xml.namespace.QName("currency")).getValue();
}
}
if (event.isCharacters()) {
Characters characters = event.asCharacters();
String value = characters.getData().trim();
if (!value.isEmpty()) {
if (currentElement.equals("title")) {
System.out.println(" Title: " + value);
} else if (currentElement.equals("author")) {
System.out.println(" Author: " + value);
} else if (currentElement.equals("year")) {
System.out.println(" Year: " + value);
} else if (currentElement.equals("price")) {
System.out.println(" Price: " + value + " (" + currentCurrency + ")");
}
}
}
if (event.isEndElement()) {
EndElement endElement = event.asEndElement();
if (endElement.getName().getLocalPart().equals("book")) {
// 一本书处理完毕,重置状态
currentCategory = null;
}
}
}
} catch (XMLStreamException | IOException e) {
e.printStackTrace();
}
}
}
优点与缺点
- 优点:
- 内存效率高:和 SAX 一样,是流式处理,内存占用小。
- 编程更直观:是“拉”模型,开发者可以控制解析的流程,比 SAX 的事件驱动更易于理解和管理。
- 性能好:通常比 SAX 更快。
- 支持读写:除了
XMLEventReader,StAX 还提供了XMLStreamWriter用于生成 XML。
- 缺点:
不如 DOM 那样可以直接随机访问和修改任意节点。
总结与如何选择
| 特性 | DOM | SAX | StAX (推荐) |
|---|---|---|---|
| 模型 | 树状模型 | 事件驱动 (推) | 事件迭代 (拉) |
| 内存占用 | 高 (整个文档) | 低 (当前节点) | 低 (当前节点) |
| 性能 | 较慢 | 快 | 很快 |
| 修改能力 | 可以 | 不可以 | 可以 (通过 XMLStreamWriter) |
| 易用性 | 非常直观 | 复杂 | 较为直观 |
| 适用场景 | 小型 XML 文档,需要频繁读写和修改 | 大型只读 XML 文档,内存受限环境 | 大多数场景,特别是需要高性能和流式处理的场景 |
选择建议:
- 如果你的 XML 文档很小(比如几KB到几MB),并且你需要对它进行多次查询、修改或操作,使用 DOM 是最简单直接的选择。
- 如果你的 XML 文档非常大(几十MB以上),并且你只需要读取其中的数据,使用 SAX 或 StAX,如果处理逻辑复杂,StAX 通常是更好的选择,因为它更灵活、易用。
- 在绝大多数现代 Java 应用中,StAX 被认为是解析 XML 的最佳实践,因为它在性能、内存占用和易用性之间取得了很好的平衡。
对于更复杂的对象映射需求,还可以考虑使用 JAXB (Java Architecture for XML Binding),它能将 XML 字符串直接映射成 Java 对象,进一步简化代码。

