0%

DOM解析

DOM 解析:XML 文档的树形结构解析方式

DOM(Document Object Model,文档对象模型)是一种基于树形结构的 XML 解析标准,它将整个 XML 文档加载到内存中并构建一个完整的节点树,允许开发者通过树结构随机访问、修改和操作 XML 中的任何节点。这种解析方式直观且功能强大,是 XML 处理的重要手段之一。

DOM 解析的核心原理

DOM 解析的核心思想是将 XML 文档映射为一棵节点树,树中的每个节点对应 XML 文档的一个组成部分(如元素、属性、文本等)。开发者可以通过操作这棵树来访问和修改 XML 内容,具体流程如下:

  1. 加载文档:DOM 解析器读取整个 XML 文档,将其加载到内存中。
  2. 构建树结构:根据 XML 的层级关系,在内存中构建一棵 DOM 树,根节点为整个文档,各级子节点对应 XML 的元素、属性等。
  3. 操作节点:通过 DOM 提供的 API 遍历、查询、添加、修改或删除树中的节点。
  4. 持久化:(可选)将修改后的 DOM 树重新写入文件或输出流。

DOM 核心接口与类

Java 中 DOM 解析主要通过javax.xml.parsers包中的接口实现,核心组件包括:

接口 / 类 作用
DocumentBuilderFactory DOM 解析器的工厂类,用于创建DocumentBuilder
DocumentBuilder DOM 解析器的核心接口,负责解析 XML 并生成Document对象
Document 代表整个 XML 文档,是 DOM 树的根节点,提供创建和查询节点的方法
Node 所有 DOM 节点的父接口,定义节点的通用方法(如appendChildgetNodeValue
NodeList 节点集合,用于存储多个节点(如元素的子节点)
Element 元素节点的接口,继承自Node,提供属性操作等方法
Attr 属性节点的接口,代表元素的属性

关键接口方法解析

1. Document接口(核心方法)
  • getElementsByTagName(String tagname):根据标签名获取所有匹配的元素节点集合(NodeList)。
  • createElement(String tagName):创建指定名称的元素节点。
  • createTextNode(String data):创建文本节点。
  • createAttribute(String name):创建属性节点。
2. Node接口(核心方法)
  • appendChild(Node newChild):向当前节点添加子节点。
  • getChildNodes():获取当前节点的所有子节点(NodeList)。
  • getAttributes():获取当前节点的所有属性(NamedNodeMap)。
  • getNodeName():获取节点名称(如元素名、属性名)。
  • getNodeValue():获取节点值(如文本内容、属性值)。
  • hasChildNodes():判断当前节点是否有子节点。
3. NodeList接口(核心方法)
  • getLength():获取节点集合的长度。
  • item(int index):根据索引获取指定节点(索引从 0 开始)。

DOM 解析实战示例

以 MyBatis 的mapper.xml为例,演示 DOM 解析的常用操作:

示例 XML 文档(UserMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.UserMapper">
<resultMap type="com.example.User" id="userMap">
<id column="id" property="id"/>
<result column="name" property="name"/>
</resultMap>

<select id="getUser" resultMap="userMap">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>

解析 XML 并读取节点内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;

public class DOMParserExample {
public static void main(String[] args) {
try {
// 1. 创建解析器工厂和解析器
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();

// 2. 解析XML文件,生成Document对象(DOM树)
Document document = builder.parse(new File("UserMapper.xml"));

// 3. 读取根节点<mapper>的namespace属性
NodeList mapperList = document.getElementsByTagName("mapper");
Node mapperNode = mapperList.item(0); // 获取第一个mapper节点
if (mapperNode.hasAttributes()) {
Node namespaceNode = mapperNode.getAttributes().getNamedItem("namespace");
System.out.println("namespace: " + namespaceNode.getNodeValue()); // 输出:com.example.UserMapper
}

// 4. 遍历<mapper>的子节点(resultMap、select等)
NodeList childNodes = mapperNode.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
// 忽略空白节点(文本节点中的空格、换行)
if (child.getNodeType() != Node.ELEMENT_NODE) continue;

String nodeName = child.getNodeName();
if ("resultMap".equals(nodeName)) {
// 读取resultMap的id属性
String id = child.getAttributes().getNamedItem("id").getNodeValue();
System.out.println("resultMap id: " + id); // 输出:userMap
} else if ("select".equals(nodeName)) {
// 读取select的id和文本内容
String id = child.getAttributes().getNamedItem("id").getNodeValue();
String sql = child.getTextContent().trim(); // 去除空白
System.out.println("select id: " + id); // 输出:getUser
System.out.println("SQL: " + sql); // 输出:SELECT * FROM user WHERE id = #{id}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

创建和修改 DOM 节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;

public class DOMCreatorExample {
public static void main(String[] args) {
try {
// 1. 创建空的Document对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.newDocument(); // 创建空白文档

// 2. 创建根节点<book>并设置属性
Element root = document.createElement("book");
root.setAttribute("id", "1001"); // 设置属性
document.appendChild(root); // 将根节点添加到文档

// 3. 创建子节点<title>和<author>
Element title = document.createElement("title");
title.setTextContent("XML入门指南"); // 设置文本内容
root.appendChild(title);

Element author = document.createElement("author");
author.setTextContent("张三");
root.appendChild(author);

// 4. 将DOM树写入文件(持久化)
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
// 设置XML版本和编码
transformer.setOutputProperty("version", "1.0");
transformer.setOutputProperty("encoding", "UTF-8");

DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File("new_book.xml"));
transformer.transform(source, result); // 写入文件

System.out.println("XML文件创建成功!");
} catch (Exception e) {
e.printStackTrace();
}
}
}

生成的new_book.xml内容

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<book id="1001">
<title>XML入门指南</title>
<author>张三</author>
</book>

DOM 解析器的配置

DocumentBuilderFactory提供了多种配置方法,用于定制解析器行为:

方法 作用 默认值
setNamespaceAware(boolean aware) 是否支持 XML 命名空间 false
setValidating(boolean validating) 是否验证 XML 文档(根据 DTD/Schema) false
setIgnoringComments(boolean ignore) 是否忽略注释节点 false
setIgnoringElementContentWhitespace(boolean ignore) 是否忽略元素内容中的空白节点 false
setCoalescing(boolean coalescing) 是否将 CDATA 节点合并为文本节点 false

示例:配置支持命名空间并忽略注释

1
2
3
4
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true); // 支持命名空间
factory.setIgnoringComments(true); // 忽略注释
DocumentBuilder builder = factory.newDocumentBuilder();

DOM 解析的优缺点

优点

  1. 随机访问:DOM 树在内存中完整存在,可通过节点间的关系(父子、兄弟)随机访问任何节点,灵活性高。
  2. 双向操作:不仅可以读取 XML,还能方便地添加、修改、删除节点,适合需要修改 XML 结构的场景。
  3. 直观易用:树形结构与 XML 的层级关系一致,符合人类对文档结构的理解。

缺点

  1. 内存消耗大:需要将整个 XML 文档加载到内存并构建 DOM 树,对于大型 XML(如几百 MB)可能导致内存溢出。
  2. 解析速度慢:加载和构建大型 DOM 树需要更多时间,效率低于流式解析(如 SAX)。

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10