什么是 DOM 解析?
DOM (Document Object Model, 文档对象模型) 是一种与平台和语言无关的接口,它将 XML 文档(或 HTML 文档)解析成一个树形结构,这个树由节点(Node)组成,每个节点代表 XML 文档中的一个元素、属性、文本或其他部分。

核心思想: 将整个 XML 文档一次性加载到内存中,构建一个完整的树形结构,你可以通过遍历这棵树来访问、查询、修改或删除文档中的任何部分。
优点:
- 结构清晰: 树形结构直观,易于理解和操作。
- 随机访问: 可以随时访问树中的任意节点,无需像 SAX 那样从头开始遍历。
- 支持修改: 可以方便地修改 XML 文档的结构和内容。
缺点:
- 内存消耗大: 对于大型 XML 文件,将其全部加载到内存中可能会消耗大量资源,甚至导致内存溢出(OutOfMemoryError)。
- 性能较低: 因为需要一次性加载整个文件,所以解析速度相对较慢。
DOM 解析器非常适合处理小型到中等规模的 XML 文件。

准备工作:XML 示例文件
我们使用一个名为 students.xml 的文件作为示例。
students.xml
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="S001">
<name>张三</name>
<age>20</age>
<gender>男</gender>
</student>
<student id="S002">
<name>李四</name>
<age>22</age>
<gender>女</gender>
</student>
<student id="S003">
<name>王五</name>
<age>21</age>
<gender>男</gender>
</student>
</students>
DOM 解析步骤
使用 Java DOM 解析 XML 主要分为以下几步:
- 获取
DocumentBuilderFactory实例:这是一个工厂类,用于创建DocumentBuilder。 - 获取
DocumentBuilder实例:这是实际的解析器。 - 解析 XML 文件,获取
Document对象:Document对象代表了整个 XML 文档的树形结构。 - 获取文档的根节点:通常是
<students>- 遍历节点树,提取数据:通过
getElementsByTagName()或getChildNodes()等方法查找和访问节点。- 获取节点的属性和文本内容。
- (可选)修改 XML:可以创建新节点、修改节点内容或属性,然后将修改后的
Document对象写回 XML 文件。 - 遍历节点树,提取数据:通过
完整代码示例
下面是一个完整的 Java 程序,演示如何解析 students.xml 文件,并打印出每个学生的信息。

DOMParserDemo.java
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
public class DOMParserDemo {
public static void main(String[] args) {
try {
// 1. 创建一个 DocumentBuilderFactory 对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2. 创建一个 DocumentBuilder 对象
DocumentBuilder builder = factory.newDocumentBuilder();
// 3. 解析 XML 文件,得到一个 Document 对象(整个 XML 文档的树形结构)
// 请确保 students.xml 文件位于项目根目录下,或者提供正确的文件路径
Document document = builder.parse(new File("students.xml"));
// 4. 获取文档的根节点 <students>
Element root = document.getDocumentElement();
// 打印根节点名称
System.out.println("根节点: " + root.getNodeName());
// 5. 获取所有名为 "student" 的节点列表
NodeList studentList = root.getElementsByTagName("student");
System.out.println("\n--- 开始解析学生信息 ---");
// 6. 遍历 studentList
for (int i = 0; i < studentList.getLength(); i++) {
// 将每个节点转换为 Element 元素,以便操作其属性
Element studentElement = (Element) studentList.item(i);
// 7. 获取节点的属性 "id"
String id = studentElement.getAttribute("id");
System.out.println("学生ID: " + id);
// 8. 获取子节点 <name>, <age>, <gender>
// getElementsByTagName() 会在该元素的子树中查找
NodeList nameList = studentElement.getElementsByTagName("name");
NodeList ageList = studentElement.getElementsByTagName("age");
NodeList genderList = studentElement.getElementsByTagName("gender");
// 9. 获取第一个子节点的文本内容
// 注意:XML 标签内的文本是作为该元素的子 TextNode 存在的
String name = nameList.item(0).getTextContent();
String age = ageList.item(0).getTextContent();
String gender = genderList.item(0).getTextContent();
System.out.println(" 姓名: " + name);
System.out.println(" 年龄: " + age);
System.out.println(" 性别: " + gender);
System.out.println("------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解析
DocumentBuilderFactory.newInstance(): 获取一个工厂实例,这是一个标准的工厂模式实现。factory.newDocumentBuilder(): 使用工厂创建解析器DocumentBuilder。builder.parse(new File("students.xml")): 这是核心步骤,解析器读取文件并构建内存中的 DOM 树,返回一个Document对象。document.getDocumentElement(): 获取文档的根元素,即<students>。root.getElementsByTagName("student"): 从根节点开始,查找所有名为 "student" 的元素,返回一个NodeList。for (int i = 0; i < studentList.getLength(); i++): 遍历NodeList,处理每个<student>节点。studentElement.getAttribute("id"): 获取当前<student>元素的id属性的值。studentElement.getElementsByTagName("name"): 在当前<student>元素内部查找<name>子节点。nameList.item(0).getTextContent():item(0)获取NodeList中的第一个节点(XML 中一个标签名是唯一的,所以只有一个)。getTextContent()获取该节点及其所有子节点的文本内容,对于<name>张三</name>,它会返回 "张三"。
运行结果
假设 students.xml 文件和 DOMParserDemo.java 在同一个目录下,运行 DOMParserDemo.java,输出如下:
根节点: students
--- 开始解析学生信息 ---
学生ID: S001
姓名: 张三
年龄: 20
性别: 男
------------------------
学生ID: S002
姓名: 李四
年龄: 22
性别: 女
------------------------
学生ID: S003
姓名: 王五
年龄: 21
性别: 男
------------------------
进阶:修改 XML 并保存
DOM 的一个强大之处在于可以修改文档,下面是一个示例,展示如何添加一个新学生,并将修改后的内容保存到新文件中。
ModifyAndSaveXML.java
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
public class ModifyAndSaveXML {
public static void main(String[] args) {
try {
// 1. 解析现有文件
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File("students.xml"));
Element root = document.getDocumentElement();
// 2. 创建新学生节点 <student>
Element newStudent = document.createElement("student");
newStudent.setAttribute("id", "S004");
// 3. 创建新学生的子节点 <name>, <age>, <gender>
Element name = document.createElement("name");
name.setTextContent("赵六");
Element age = document.createElement("age");
age.setTextContent("23");
Element gender = document.createElement("gender");
gender.setTextContent("女");
// 4. 将子节点添加到 <student> 节点
newStudent.appendChild(name);
newStudent.appendChild(age);
newStudent.appendChild(gender);
// 5. 将新学生节点添加到根节点 <students>
root.appendChild(newStudent);
// 6. 将修改后的 DOM 树写入新文件
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 格式化输出
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File("students_modified.xml"));
transformer.transform(source, result);
System.out.println("XML 文件已成功修改并保存为 students_modified.xml");
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行后生成的新文件 students_modified.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<students>
<student id="S001">
<name>张三</name>
<age>20</age>
<gender>男</gender>
</student>
<student id="S002">
<name>李四</name>
<age>22</age>
<gender>女</gender>
</student>
<student id="S003">
<name>王五</name>
<age>21</age>
<gender>男</gender>
</student>
<student id="S004">
<name>赵六</name>
<age>23</age>
<gender>女</gender>
</student>
</students>
DOM vs. SAX (简短对比)
| 特性 | DOM (Document Object Model) | SAX (Simple API for XML) |
|---|---|---|
| 工作方式 | 将整个 XML 文档加载到内存,构建树形结构。 | 事件驱动,逐行读取 XML 文件,不保存文档内容。 |
| 内存占用 | 高,与文件大小成正比。 | 低,内存占用恒定,适合大文件。 |
| 访问方式 | 随机访问,可以前后遍历。 | 顺序访问,只能从头到尾一次。 |
| 修改能力 | 可以方便地修改、添加、删除节点。 | 不可以修改文档,只能读取数据。 |
| 解析速度 | 较慢。 | 较快。 |
| 适用场景 | 小型/中型 XML 文件,需要频繁访问或修改文档内容。 | 大型 XML 文件,只需要读取一次数据,对内存敏感。 |
Java 原生的 DOM API 功能强大且灵活,是处理 XML 的标准方式之一,对于中小型 XML 文件,它是一个非常直观和有效的选择,如果你的应用需要处理 GB 级别的大型 XML 文件,或者内存非常有限,那么应该考虑使用 SAX 或 StAX 解析器,在现代 Java 开发中,也有一些更高级的库(如 JAXB 用于 XML 与 Java 对象的绑定,JDOM 和 DOM4J 作为对标准 DOM 的增强和简化),但理解基础的 DOM 原理仍然是至关重要的。
