DOM(Document Object Model,文档对象模型)是 W3C 组织推荐的处理 XML 的一种方式,它将整个 XML 文档加载到内存中,解析成一个树形结构,你可以像操作树一样,轻松地访问、添加、修改或删除文档中的任何节点。

核心特点:
- 树形结构:整个 XML 文档被转换成一个由节点(Node)组成的树。
- 内存消耗大:因为要把整个文档都加载到内存,所以对于非常大的 XML 文件,DOM 解析可能会消耗大量内存,甚至导致内存溢出。
- 随机访问:由于整个文档都在内存中,你可以随时访问任何一个节点,操作非常灵活。
准备工作:引入依赖
标准的 Java 开发环境(JDK)已经包含了处理 XML 所需的 org.w3c.dom 和 javax.xml.parsers 等包,所以你不需要额外引入任何依赖。
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>21</age>
<gender>女</gender>
</student>
</students>
解析 XML (读)
解析 XML 的主要步骤是:
- 创建一个
DocumentBuilderFactory实例。 - 通过工厂创建一个
DocumentBuilder实例。 - 使用
DocumentBuilder的parse()方法解析 XML 文件,得到一个Document对象(即整个 XML 的内存树)。 - 通过
Document对象获取根节点,然后通过节点方法(如getElementsByTagName(),getChildNodes()等)遍历和查找节点。
示例代码:读取 students.xml
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
public class ReadXmlWithDom {
public static void main(String[] args) {
try {
// 1. 创建 DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2. 创建 DocumentBuilder
DocumentBuilder builder = factory.newDocumentBuilder();
// 3. 解析 XML 文件,得到 Document 对象
// File xmlFile = new File("students.xml");
// Document document = builder.parse(xmlFile);
// 也可以从 URL 或输入流解析
Document document = builder.parse(new File("students.xml"));
// 4. 获取文档的根节点
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++) {
Node studentNode = studentList.item(i);
// 确保节点是 Element 类型
if (studentNode.getNodeType() == Node.ELEMENT_NODE) {
Element studentElement = (Element) studentNode;
// 获取 id 属性
String id = studentElement.getAttribute("id");
System.out.println("学生ID: " + id);
// 获取 name, age, gender 子节点的文本内容
String name = studentElement.getElementsByTagName("name").item(0).getTextContent();
String age = studentElement.getElementsByTagName("age").item(0).getTextContent();
String gender = studentElement.getElementsByTagName("gender").item(0).getTextContent();
System.out.println(" 姓名: " + name);
System.out.println(" 年龄: " + age);
System.out.println(" 性别: " + gender);
System.out.println("--------------------");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解释:
DocumentBuilderFactory和DocumentBuilder是创建解析器的标准工厂模式。builder.parse(file)将 XML 文件解析成Document对象。document.getDocumentElement()获取 XML 的根元素(在这里是<students>)。root.getElementsByTagName("student")获取所有名为student的元素节点,返回一个NodeList。node.getNodeType() == Node.ELEMENT_NODE用于判断节点是否是元素节点(<tag>),而不是文本节点(如换行符和空格)或注释节点。element.getAttribute("id")获取元素的属性值。element.getElementsByTagName("name").item(0)获取第一个匹配的子元素节点。.getTextContent()获取该节点及其所有子节点的文本内容合并后的字符串。
生成 XML (写)
生成 XML 的主要步骤是:

- 同样,创建
DocumentBuilderFactory和DocumentBuilder。 - 使用
DocumentBuilder的newDocument()方法创建一个空的Document对象。 - 使用
Document接口提供的createElement(),createTextNode(),setAttribute()等方法在内存中构建 XML 树。 - 使用
TransformerFactory和Transformer将内存中的Document对象写入到文件或输出流中。
示例代码:创建一个新的 new_students.xml 文件
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 WriteXmlWithDom {
public static void main(String[] args) {
try {
// 1. 创建 DocumentBuilderFactory 和 DocumentBuilder
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 2. 创建一个新的 Document 对象
Document document = builder.newDocument();
// 3. 创建根元素 <classroom>
Element rootElement = document.createElement("classroom");
document.appendChild(rootElement);
// 4. 创建第一个学生 <student>
Element student1 = document.createElement("student");
student1.setAttribute("id", "C001");
Element name1 = document.createElement("name");
name1.appendChild(document.createTextNode("王五"));
Element age1 = document.createElement("age");
age1.appendChild(document.createTextNode("22"));
student1.appendChild(name1);
student1.appendChild(age1);
rootElement.appendChild(student1);
// 5. 创建第二个学生 <student>
Element student2 = document.createElement("student");
student2.setAttribute("id", "C002");
Element name2 = document.createElement("name");
name2.appendChild(document.createTextNode("赵六"));
Element age2 = document.createElement("age");
age2.appendChild(document.createTextNode("23"));
student2.appendChild(name2);
student2.appendChild(age2);
rootElement.appendChild(student2);
// 6. 将 Document 对象写入 XML 文件
// 创建 TransformerFactory
TransformerFactory transformerFactory = TransformerFactory.newInstance();
// 创建 Transformer
Transformer transformer = transformerFactory.newTransformer();
// 设置输出格式 (可选,但推荐)
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 格式化输出
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); // 缩进量
// 创建 DOMSource (源)
DOMSource source = new DOMSource(document);
// 创建 StreamResult (目标)
StreamResult result = new StreamResult(new File("new_students.xml"));
// 执行转换
transformer.transform(source, result);
System.out.println("XML 文件生成成功: new_students.xml");
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解释:
builder.newDocument()创建一个空的文档。document.createElement("tag")创建一个新的元素节点。document.createTextNode("text")创建一个文本节点。element.appendChild(node)将一个子节点添加到父节点。element.setAttribute("key", "value")为元素添加属性。Transformer和TransformerFactory是将内存中的 DOM 树序列化为 XML 文档的核心。DOMSource:表示转换的源,即我们创建的Document对象。StreamResult:表示转换的目标,可以是一个File、OutputStream或Writer。transformer.transform(source, result):执行转换操作。
执行后,会生成一个 new_students.xml 文件,内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<classroom>
<student id="C001">
<name>王五</name>
<age>22</age>
</student>
<student id="C002">
<name>赵六</name>
<age>23</age>
</student>
</classroom>
修改现有 XML
修改 XML 结合了“读”和“写”的步骤,先读取现有 XML 到内存,然后修改 DOM 树,最后再写回文件。
示例代码:在 students.xml 中添加一个新学生
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 ModifyXmlWithDom {
public static void main(String[] args) {
try {
// 1. 解析现有文件
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File("students.xml"));
// 2. 获取根节点
Element root = document.getDocumentElement();
// 3. 创建新学生节点
Element newStudent = document.createElement("student");
newStudent.setAttribute("id", "S003");
Element name = document.createElement("name");
name.appendChild(document.createTextNode("孙七"));
newStudent.appendChild(name);
Element age = document.createElement("age");
age.appendChild(document.createTextNode("19"));
newStudent.appendChild(age);
Element gender = document.createElement("gender");
gender.appendChild(document.createTextNode("男"));
newStudent.appendChild(gender);
// 4. 将新学生添加到根节点
root.appendChild(newStudent);
// 5. 将修改后的 Document 写回文件
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File("students.xml")); // 覆盖原文件
transformer.transform(source, result);
System.out.println("XML 文件修改成功,新学生已添加。");
} catch (Exception e) {
e.printStackTrace();
}
}
}
DOM vs. SAX (简单对比)
在选择解析器时,了解 DOM 和 SAX 的区别很重要。
| 特性 | DOM (Document Object Model) | SAX (Simple API for XML) |
|---|---|---|
| 工作方式 | 将整个 XML 文档加载到内存,构建树形结构。 | 事件驱动,解析器从头到尾读取 XML,当遇到元素开始、结束、文本等时,触发相应的事件。 |
| 内存占用 | 高,与文件大小成正比,大文件可能导致内存溢出。 | 低,解析时只保留当前节点信息,内存占用恒定。 |
| 访问方式 | 随机访问,可以随时访问任意节点。 | 顺序访问,只能从前往后遍历,无法回头。 |
| 操作方式 | 易于修改,可以直接在内存树中增删改节点。 | 只读,不适合修改 XML,适合提取数据。 |
| 速度 | 对于小文件,解析速度较快,对于大文件,因加载慢而整体较慢。 | 解析速度通常很快,尤其适合大文件。 |
| 适用场景 | XML 文件不大,需要频繁访问和修改数据。 | XML 文件很大,只需要读取一次并提取特定信息。 |
- 如果你的 XML 文件不大,并且需要对它进行复杂的查询和修改,DOM 是最佳选择,因为它简单直观。
- 如果你的 XML 文件非常大(几百MB或GB级别),或者你只需要读取其中的部分数据,SAX 或 Pull 解析器会更高效。
