- DOM (Document Object Model):将整个 XML 文档读入内存,构建一个树形结构,优点是操作灵活,可以随意增删改查节点;缺点是当 XML 文件很大时,会占用大量内存,可能导致性能问题甚至内存溢出。
- SAX (Simple API for XML):一种事件驱动的解析模型,它不会将整个文档加载到内存,而是按顺序读取 XML 文档,当解析到某个元素(如开始标签、结束标签、文本内容等)时,会触发相应的事件,优点是内存占用非常小,适合解析大文件;缺点是只能顺序读取,不能随机访问,操作起来比 DOM 复杂。
- StAX (Streaming API for XML):介于 DOM 和 SAX 之间,也是一种流式 API,但它不像 SAX 那样被动地接收事件,而是允许应用程序主动“拉取”(pull)事件,优点是内存效率高,并且比 SAX 更易于编程控制。
下面我将分别介绍如何使用这三种方式来解析一个 XML 字符串,并提供完整的代码示例。

准备工作:示例 XML 字符串
我们将使用以下这个简单的 XML 字符串作为所有示例的输入:
<?xml version="1.0" encoding="UTF-8"?>
<library>
<book id="001">
<title>Java Programming</title>
<author>John Doe</author>
<price currency="USD">39.99</price>
</book>
<book id="002">
<title>Effective Java</title>
<author>Joshua Bloch</author>
<price currency="EUR">45.50</price>
</book>
</library>
使用 DOM 解析
DOM 解析器会将整个 XML 字符串解析成一个 Document 对象,然后我们可以像操作树一样操作它。
步骤:
- 获取
DocumentBuilderFactory实例。 - 创建
DocumentBuilder。 - 将 XML 字符串解析为
Document对象。 - 通过
Document对象获取节点(如Element),然后遍历和提取数据。
示例代码:
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 DomParserExample {
public static void main(String[] args) {
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<library>\n" +
" <book id=\"001\">\n" +
" <title>Java Programming</title>\n" +
" <author>John Doe</author>\n" +
" <price currency=\"USD\">39.99</price>\n" +
" </book>\n" +
" <book id=\"002\">\n" +
" <title>Effective Java</title>\n" +
" <author>Joshua Bloch</author>\n" +
" <price currency=\"EUR\">45.50</price>\n" +
" </book>\n" +
"</library>";
try {
// 1. 创建 DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2. 创建 DocumentBuilder
DocumentBuilder builder = factory.newDocumentBuilder();
// 3. 将 XML 字符串解析为 Document 对象
// 使用 ByteArrayInputStream 将字符串转换为 InputStream
Document document = builder.parse(new ByteArrayInputStream(xmlString.getBytes("UTF-8")));
// 4. 获取所有 book 节点
NodeList bookList = document.getElementsByTagName("book");
for (int i = 0; i < bookList.getLength(); i++) {
Node node = bookList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element bookElement = (Element) node;
// 获取 id 属性
String id = bookElement.getAttribute("id");
// 获取子元素的内容
String title = bookElement.getElementsByTagName("title").item(0).getTextContent();
String author = bookElement.getElementsByTagName("author").item(0).getTextContent();
// 获取 price 元素及其属性
Element priceElement = (Element) bookElement.getElementsByTagName("price").item(0);
String price = priceElement.getTextContent();
String currency = priceElement.getAttribute("currency");
System.out.println("Book ID: " + id);
System.out.println(" Title: " + title);
System.out.println(" Author: " + author);
System.out.println(" Price: " + price + " " + currency);
System.out.println("-----------------------------");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
依赖:DOM 解析是 Java 标准库的一部分,无需额外依赖。
使用 SAX 解析
SAX 解析器是事件驱动的,我们需要定义一个 Handler 来处理不同的事件(如开始元素、结束元素、遇到文本等)。

步骤:
- 创建
SAXParserFactory实例。 - 创建
SAXParser。 - 创建自定义的
DefaultHandler子类,并重写其事件处理方法(如startElement,endElement,characters)。 - 将
Handler和 XML 字符串的输入流传递给SAXParser进行解析。
示例代码:
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.util.Stack;
public class SaxParserExample {
public static void main(String[] args) {
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<library>\n" +
" <book id=\"001\">\n" +
" <title>Java Programming</title>\n" +
" <author>John Doe</author>\n" +
" <price currency=\"USD\">39.99</price>\n" +
" </book>\n" +
" <book id=\"002\">\n" +
" <title>Effective Java</title>\n" +
" <author>Joshua Bloch</author>\n" +
" <price currency=\"EUR\">45.50</price>\n" +
" </book>\n" +
"</library>";
try {
// 1. 创建 SAXParserFactory
SAXParserFactory factory = SAXParserFactory.newInstance();
// 2. 创建 SAXParser
SAXParser saxParser = factory.newSAXParser();
// 3. 创建自定义的 Handler
DefaultHandler handler = new DefaultHandler() {
// 使用一个栈来跟踪当前正在处理的元素
private Stack<String> elementStack = new Stack<>();
private String currentElement;
private Book currentBook;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
currentElement = qName;
elementStack.push(qName);
if ("book".equals(qName)) {
currentBook = new Book();
currentBook.setId(attributes.getValue("id"));
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
elementStack.pop();
if ("book".equals(qName)) {
System.out.println(currentBook);
currentBook = null;
}
currentElement = null;
}
@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 (currentBook != null) {
switch (currentElement) {
case "title":
currentBook.setTitle(value);
break;
case "author":
currentBook.setAuthor(value);
break;
case "price":
currentBook.setPrice(value);
currentBook.setCurrency(currentBook.getCurrency()); // 属性在 startElement 中处理
break;
}
}
}
// 重写 startElement 来处理属性
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
currentElement = qName;
elementStack.push(qName);
if ("book".equals(qName)) {
currentBook = new Book();
currentBook.setId(attributes.getValue("id"));
} else if ("price".equals(qName) && currentBook != null) {
currentBook.setCurrency(attributes.getValue("currency"));
}
}
};
// 4. 解析 XML
saxParser.parse(new ByteArrayInputStream(xmlString.getBytes("UTF-8")), handler);
} catch (Exception e) {
e.printStackTrace();
}
}
// 一个简单的 Book 类来存储数据
static class Book {
private String id;
private String title;
private String author;
private String price;
private String currency;
// Getters and Setters
public void setId(String id) { this.id = id; }
public void setTitle(String title) { this.title = title; }
public void setAuthor(String author) { this.author = author; }
public void setPrice(String price) { this.price = price; }
public void setCurrency(String currency) { this.currency = currency; }
@Override
public String toString() {
return "Book ID: " + id +
"\n Title: " + title +
"\n Author: " + author +
"\n Price: " + price + " " + currency +
"\n-----------------------------";
}
}
}
依赖:SAX 解析也是 Java 标准库的一部分,无需额外依赖。
使用 StAX 解析
StAX 解析器允许我们“拉取”事件,我们创建一个 XMLStreamReader,然后在一个循环中不断调用 next() 方法来获取下一个事件,并根据事件类型进行处理。
步骤:
- 创建
XMLInputFactory实例。 - 通过
factory.createXMLEventReader()或factory.createXMLStreamReader()创建读取器。 - 使用
while (reader.hasNext())循环,调用reader.next()获取事件。 - 使用
if (eventType == XMLEvent.START_ELEMENT)等判断事件类型,并处理。
示例代码:
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
public class StaxParserExample {
public static void main(String[] args) {
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<library>\n" +
" <book id=\"001\">\n" +
" <title>Java Programming</title>\n" +
" <author>John Doe</author>\n" +
" <price currency=\"USD\">39.99</price>\n" +
" </book>\n" +
" <book id=\"002\">\n" +
" <title>Effective Java</title>\n" +
" <author>Joshua Bloch</author>\n" +
" <price currency=\"EUR\">45.50</price>\n" +
" </book>\n" +
"</library>";
List<Book> books = new ArrayList<>();
Book currentBook = null;
String currentElementName = null;
try {
// 1. 创建 XMLInputFactory
XMLInputFactory factory = XMLInputFactory.newInstance();
// 2. 创建 XMLEventReader
XMLEventReader eventReader = factory.createXMLEventReader(new ByteArrayInputStream(xmlString.getBytes("UTF-8")));
// 3. 遍历事件
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
switch (event.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
currentElementName = event.asStartElement().getName().getLocalPart();
if ("book".equals(currentElementName)) {
currentBook = new Book();
currentBook.setId(event.asStartElement().getAttributeByName("id").getValue());
}
break;
case XMLStreamConstants.CHARACTERS:
String characters = event.asCharacters().getData().trim();
if (currentBook != null && !characters.isEmpty()) {
switch (currentElementName) {
case "title":
currentBook.setTitle(characters);
break;
case "author":
currentBook.setAuthor(characters);
break;
case "price":
currentBook.setPrice(characters);
// 属性在 START_ELEMENT 事件中处理
break;
}
}
break;
case XMLStreamConstants.END_ELEMENT:
if ("book".equals(event.asEndElement().getName().getLocalPart())) {
books.add(currentBook);
currentBook = null;
}
currentElementName = null; // 重置当前元素名
break;
}
}
// 打印结果
for (Book book : books) {
System.out.println(book);
}
} catch (XMLStreamException e) {
e.printStackTrace();
}
}
// Book 类
static class Book {
private String id;
private String title;
private String author;
private String price;
private String currency;
// Getters and Setters
public void setId(String id) { this.id = id; }
public void setTitle(String title) { this.title = title; }
public void setAuthor(String author) { this.author = author; }
public void setPrice(String price) { this.price = price; }
public void setCurrency(String currency) { this.currency = currency; }
public void setCurrency(String currency) { this.currency = currency; } // 假设在 price 的 start_element 中设置
@Override
public String toString() {
return "Book ID: " + id +
"\n Title: " + title +
"\n Author: " + author +
"\n Price: " + price + " " + currency +
"\n-----------------------------";
}
}
}
注意:上面的 StAX 示例中,currency 属性的处理需要稍作调整,应该在遇到 <price> 标签的 START_ELEMENT 事件时获取属性,这里为了简化,逻辑上可以调整一下,或者像 SAX 一样在 START_ELEMENT 中处理。
使用第三方库:Jackson / Gson (JSON 风格)
在现代 Java 开发中,更推荐使用第三方库,如 Jackson 或 Gson,它们可以将 XML 直接映射到 Java 对象(POJO),代码更简洁,可读性更高。

依赖 (Maven)
在 pom.xml 中添加 Jackson 的 XML 模块依赖:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.15.2</version> <!-- 使用最新版本 -->
</dependency>
示例代码:
-
定义 Java 对象 (POJO)
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import java.util.List; // 根注解,表示 <library> 标签对应这个类 @JacksonXmlRootElement(localName = "library") public class Library { private List<Book> book; public List<Book> getBook() { return book; } public void setBook(List<Book> book) { this.book = book; } } class Book { // @JacksonXmlProperty(isAttribute = true) 表示这是标签的属性 @JacksonXmlProperty(isAttribute = true) private String id; // localName 指定 XML 标签的名称 @JacksonXmlProperty(localName = "title") private String title; @JacksonXmlProperty(localName = "author") private String author; @JacksonXmlProperty(localName = "price") private Price price; // Getters and Setters... // 为了简化,省略了,实际项目中需要 } class Price { @JacksonXmlProperty(isAttribute = true) private String currency; @JacksonXmlProperty(localName = "#text") // 表示标签内的文本内容 private String value; // Getters and Setters... } -
解析代码
import com.fasterxml.jackson.dataformat.xml.XmlMapper; import java.io.IOException; public class JacksonXmlParserExample { public static void main(String[] args) { String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<library>\n" + " <book id=\"001\">\n" + " <title>Java Programming</title>\n" + " <author>John Doe</author>\n" + " <price currency=\"USD\">39.99</price>\n" + " </book>\n" + " <book id=\"002\">\n" + " <title>Effective Java</title>\n" + " <author>Joshua Bloch</author>\n" + " <price currency=\"EUR\">45.50</price>\n" + " </book>\n" + "</library>"; XmlMapper xmlMapper = new XmlMapper(); try { // 一行代码将 XML 字符串转换为 Java 对象 Library library = xmlMapper.readValue(xmlString, Library.class); // 现在可以像操作普通 Java 对象一样操作数据了 for (Book book : library.getBook()) { System.out.println("Book ID: " + book.getId()); System.out.println(" Title: " + book.getTitle()); System.out.println(" Author: " + book.getAuthor()); System.out.println(" Price: " + book.getPrice().getValue() + " " + book.getPrice().getCurrency()); System.out.println("-----------------------------"); } } catch (IOException e) { e.printStackTrace(); } } }
总结与如何选择
| 特性 | DOM | SAX | StAX | Jackson/Gson |
|---|---|---|---|---|
| 内存占用 | 高(加载整个文档) | 低(事件驱动) | 低(流式读取) | 中(映射到对象) |
| 性能 | 较慢(需要构建树) | 快 | 快 | 非常快(尤其对于大文件) |
| 易用性 | 非常高(树形结构) | 较低(需处理事件) | 中等(主动拉取) | 非常高(声明式映射) |
| 随机访问 | 可以(通过节点路径) | 不可以(只能顺序) | 不可以(只能顺序) | 可以(通过对象属性) |
| 修改/创建XML | 容易 | 困难 | 较困难 | 容易(修改对象后重新序列化) |
| 适用场景 | XML 文档较小,需要频繁查询和修改数据。 | XML 文档非常大,内存受限,只需读取一次。 | 需要流式处理,比 SAX 更精细的控制。 | 现代 Java 项目,追求代码简洁和可维护性。 |
选择建议:
- XML 很小,或者你需要频繁地查询和修改 XML 中的数据,使用 DOM。
- 如果你要处理非常大的 XML 文件(几百 MB 或 GB 级别),并且内存非常有限,使用 SAX 或 StAX。
- 如果你想在流式处理和编程控制之间取得平衡,StAX 是一个很好的选择。
- 在绝大多数现代 Java 项目中,强烈推荐使用 Jackson 或 Gson,它们将 XML 解析从繁琐的事件处理中解放出来,让你可以专注于业务逻辑,代码更清晰、更健壮。
