DOM 解析:XML 文档的树形结构解析方式 DOM(Document Object Model,文档对象模型)是一种基于树形结构的 XML 解析标准,它将整个 XML 文档加载到内存中并构建一个完整的节点树,允许开发者通过树结构随机访问、修改和操作 XML 中的任何节点。这种解析方式直观且功能强大,是 XML 处理的重要手段之一。
DOM 解析的核心原理 DOM 解析的核心思想是将 XML 文档映射为一棵节点树 ,树中的每个节点对应 XML 文档的一个组成部分(如元素、属性、文本等)。开发者可以通过操作这棵树来访问和修改 XML 内容,具体流程如下:
加载文档 :DOM 解析器读取整个 XML 文档,将其加载到内存中。
构建树结构 :根据 XML 的层级关系,在内存中构建一棵 DOM 树,根节点为整个文档,各级子节点对应 XML 的元素、属性等。
操作节点 :通过 DOM 提供的 API 遍历、查询、添加、修改或删除树中的节点。
持久化 :(可选)将修改后的 DOM 树重新写入文件或输出流。
DOM 核心接口与类 Java 中 DOM 解析主要通过javax.xml.parsers
包中的接口实现,核心组件包括:
接口 / 类
作用
DocumentBuilderFactory
DOM 解析器的工厂类,用于创建DocumentBuilder
DocumentBuilder
DOM 解析器的核心接口,负责解析 XML 并生成Document
对象
Document
代表整个 XML 文档,是 DOM 树的根节点,提供创建和查询节点的方法
Node
所有 DOM 节点的父接口,定义节点的通用方法(如appendChild
、getNodeValue
)
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 { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(new File("UserMapper.xml" )); NodeList mapperList = document.getElementsByTagName("mapper" ); Node mapperNode = mapperList.item(0 ); if (mapperNode.hasAttributes()) { Node namespaceNode = mapperNode.getAttributes().getNamedItem("namespace" ); System.out.println("namespace: " + namespaceNode.getNodeValue()); } 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)) { String id = child.getAttributes().getNamedItem("id" ).getNodeValue(); System.out.println("resultMap id: " + id); } else if ("select" .equals(nodeName)) { String id = child.getAttributes().getNamedItem("id" ).getNodeValue(); String sql = child.getTextContent().trim(); System.out.println("select id: " + id); System.out.println("SQL: " + sql); } } } 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 { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.newDocument(); Element root = document.createElement("book" ); root.setAttribute("id" , "1001" ); document.appendChild(root); Element title = document.createElement("title" ); title.setTextContent("XML入门指南" ); root.appendChild(title); Element author = document.createElement("author" ); author.setTextContent("张三" ); root.appendChild(author); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); 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 解析的优缺点 优点
随机访问 :DOM 树在内存中完整存在,可通过节点间的关系(父子、兄弟)随机访问任何节点,灵活性高。
双向操作 :不仅可以读取 XML,还能方便地添加、修改、删除节点,适合需要修改 XML 结构的场景。
直观易用 :树形结构与 XML 的层级关系一致,符合人类对文档结构的理解。
缺点
内存消耗大 :需要将整个 XML 文档加载到内存并构建 DOM 树,对于大型 XML(如几百 MB)可能导致内存溢出。
解析速度慢 :加载和构建大型 DOM 树需要更多时间,效率低于流式解析(如 SAX)。
v1.3.10