XML作为一种经典的数据交换格式,至今仍在配置文件、接口报文、Web服务等领域大量使用。Java提供了多种解析XML的方式,其中DOM(Document Object Model)是最容易上手、也最直观的一种。它把整个XML文档读入内存,形成一棵节点树,开发者可以像操作普通对象一样操作这棵树。本文将从DOM的工作原理讲起,给出完整的代码示例,并客观分析它的优缺点和适用场景。

DOM解析XML的基本原理
DOM是W3C制定的一套标准接口,它将XML文档看作一个由节点组成的树形结构。树的根是Document对象,元素对应Element节点,文本内容对应Text节点,属性对应Attr节点。整个文档的层级关系和嵌套结构都被完整地映射到内存中,因此访问任意一个节点都不需要重新读取文件。
正因为文档被完整加载到内存,DOM支持随机访问:你可以先取第十个节点,再回头取第三个节点,甚至可以直接修改或删除某个节点后重新写回文件。这种灵活性是流式解析方式(如SAX)无法提供的。不过代价也很明显:一份100MB的XML文件,构建成DOM树后占用的内存可能是原始文件大小的3到5倍,这是由每个节点对象的额外开销造成的。
在Java中,DOM解析主要通过JAXP(Java API for XML Processing)提供的一组类完成,核心入口是DocumentBuilderFactory和DocumentBuilder。这些类都包含在JDK中,不需要额外引入第三方依赖,这也是DOM在Java世界里普及度很高的原因之一。
Java DOM解析的完整代码示例
下面通过一个具体的例子演示DOM解析的核心步骤。假设有一个students.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="1001">
<name>张三</name>
<age>20</age>
</student>
<student id="1002">
<name>李四</name>
<age>22</age>
</student>
</students>解析这个文件的Java代码如下:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
import java.io.File;
public class DomParseDemo {
public static void main(String[] args) throws Exception {
// 1. 创建解析器工厂,注意newInstance方法
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2. 通过工厂创建解析器对象
DocumentBuilder builder = factory.newDocumentBuilder();
// 3. 解析文件,得到代表整棵树的Document对象
Document document = builder.parse(new File("students.xml"));
// 4. 获取所有student元素,返回NodeList
NodeList studentList = document.getElementsByTagName("student");
for (int i = 0; i < studentList.getLength(); i++) {
Element student = (Element) studentList.item(i);
// 读取属性
String id = student.getAttribute("id");
// 读取子元素的文本内容
String name = student.getElementsByTagName("name")
.item(0).getTextContent();
String age = student.getElementsByTagName("age")
.item(0).getTextContent();
System.out.println("学号:" + id + ",姓名:" + name + ",年龄:" + age);
}
}
}代码中有几个关键点值得注意。getElementsByTagName返回的NodeList是按文档顺序排列的,配合索引可以逐个访问;getTextContent会返回元素内所有的文本内容,包括嵌套层级中的文本,比getFirstChild().getNodeValue()这种写法更简洁安全,因为它不依赖节点是否存在子节点。
还有一个新手常踩的坑:XML中的空白字符(比如标签之间的换行和缩进)会被当作Text节点保存到树中。如果直接用getFirstChild()去取name元素的文本,很可能拿到的是一个空白节点而不是文本本身。解决办法有三种:使用getTextContent()、手动遍历找到类型为Node.TEXT_NODE的节点,或者在工厂上调用factory.setIgnoringElementContentWhitespace(true)来忽略缩进空白。
除了读取,DOM还支持创建和修改节点。比如想在文档中新增一个student节点再写回文件,可以这样写:
// 创建新元素
Element newStudent = document.createElement("student");
newStudent.setAttribute("id", "1003");
Element name = document.createElement("name");
name.setTextContent("王五");
newStudent.appendChild(name);
Element age = document.createElement("age");
age.setTextContent("21");
newStudent.appendChild(age);
// 挂到根节点下
document.getDocumentElement().appendChild(newStudent);
// 写回文件
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(document),
new StreamResult(new File("students.xml")));DOM解析的优缺点分析
先说优点。第一是编程模型直观,树形结构与XML的嵌套结构一一对应,代码可读性高,学习成本低。第二是支持随机访问和双向操作,既能读也能写,还能删除、移动节点,这对需要修改XML配置文件的场景非常友好。第三是API标准化,代码在Java、JavaScript、Python等语言中概念相通,迁移成本小。
缺点方面最突出的就是内存消耗。整个文档必须在内存中完整构建,文件越大,树越庞大。解析一个几百MB的XML文件时,堆内存很容易被耗尽,抛出OutOfMemoryError。其次是解析速度较慢,一次性加载的初始开销远大于流式解析。另外,如果只需要文档中的一小部分数据,把整棵树都加载进内存显然是一种浪费。
下表对三种常见解析方式做一个对比:
| 解析方式 | 内存占用 | 读写能力 | 适用场景 |
|---|---|---|---|
| DOM | 高,整树加载 | 可读可写,随机访问 | 小中型文件,需修改文档结构 |
| SAX | 低,事件驱动 | 只读,顺序访问 | 超大文件,只需读取部分数据 |
| StAX | 低,拉式解析 | 可读可写,顺序访问 | 大文件,需要控制解析节奏 |
如何选择合适的解析方式
选择解析方案时,可以从三个维度考虑:文件大小、是否需要修改文档、访问方式是否随机。如果文件在10MB以内,且业务上需要对XML进行增删改操作,DOM是首选,代码简单直接,维护成本低。如果文件达到几百MB甚至几个GB,或者只需要提取少数字段的值,就应该转向SAX或StAX这类流式解析。
实际项目中还有一种折中做法:先用SAX流式扫描过滤出需要的片段,再对片段单独构建DOM树进行精细操作。这样既避免了整棵大树撑爆内存,又保留了对目标数据的灵活处理能力。另外,如果XML结构固定且追求开发效率,也可以考虑JAXB这类对象绑定框架,它能把XML直接映射成Java对象,进一步简化代码。
最后提醒一点,解析外部来源的XML文件时要注意安全问题。建议在工厂上关闭外部实体引用,防止XXE攻击:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 禁用DTD,防止XXE注入攻击
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);总的来说,DOM解析器是Java处理XML的基础工具,理解它的树形模型和内存特性,不仅能写出正确的解析代码,也能在架构选型时做出更合理的判断。小文件用DOM图省事,大文件换流式方案保安全,按场景做取舍才是工程上的正确姿势。
Java DOM解析XML解析DOM解析器修改时间:2026-09-09 03:18:39