XML 文档结构
本章定位 :理解 XML 文档的树形层级结构——根元素、父子嵌套、兄弟节点、DOM 七种节点类型,以及 XML 声明的格式要求。
定义与作用
XML 文档是一种 树形结构——从唯一的根元素开始,逐层展开为分支和叶子。这种结构与计算机科学中的"树"数据结构完全对应,使得程序可以用统一的递归算法遍历、查询和修改 XML。
DOM(Document Object Model)将 XML 文档映射为包含 七种节点类型 的内存树。理解这七种节点,是后续学习 XPath 定位和 DOM 编程的基础。
核心原理:从文本到树
图解释 :XML 解析器将纯文本逐行解析为内存中的节点树。文档本身是一个节点(#document),XML 声明变成处理指令节点,注释变成注释节点。属性节点挂在元素节点上但不是子节点。
语法/结构要点
XML 声明
文档第一行必须是 XML 声明(XML 1.0 中可选但强烈推荐):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
| 属性 | 说明 | 可选值 |
|---|---|---|
version | XML 版本 | "1.0" 或 "1.1" |
encoding | 字符编码 | "UTF-8", "UTF-16", "ISO-8859-1" 等 |
standalone | 是否依赖外部 DTD | "yes" 或 "no" |
standalone="yes"表示文档完全自包含,不需要外部 DTD 或实体文件。
DOM 七种节点类型
| 节点类型 | nodeType | nodeName | nodeValue |
|---|---|---|---|
| 元素 | 1 | 标签名 | null |
| 属性 | 2 | 属性名 | 属性值 |
| 文本 | 3 | #text | 文本内容 |
| CDATA | 4 | #cdata-section | CDATA 内容 |
| 实体引用 | 5 | 实体名 | null |
| 处理指令 | 7 | target | instruction |
| 注释 | 8 | #comment | 注释文本 |
| 文档 | 9 | #document | null |
树结构术语
<bookstore> ← 根元素(root element)
<book> ← book 是 bookstore 的子元素
<title>...</title> ← title 和 price 是 book 的子元素,互为兄弟
<price>...</price>
</book>
</bookstore>
| 术语 | 说明 |
|---|---|
| 根元素 | 文档最外层的唯一元素,所有其他元素的祖先 |
| 父元素 | 直接包含该元素的上一级元素 |
| 子元素 | 被直接包含的下一级元素 |
| 兄弟 | 同属一个父元素的同级元素 |
| 后代 | 所有嵌套在该元素内的元素(子、孙、曾孙...) |
| 祖先 | 所有包含该元素的上级元素(父、祖父...) |
完整示例:大翔用 DOM 树诊断配置错误
场景说明
飞翔科技的运维 大翔 接到告警:一台 Web 服务器启动失败,日志显示 XML 配置解析错误。他打开 server-config.xml,一眼看不出问题。大翔决定用 Python 解析这个文件,通过 DOM 树诊断结构异常。
操作前:有结构错误的 XML
<?xml version="1.0" encoding="UTF-8"?>
<!-- 飞翔科技服务器配置 -->
<server-cluster>
<server id="web01">
<host>192.168.1.10</host>
<port>8080</port>
<load-balancer id="lb01">
<algorithm>round-robin</algorithm>
<port>80</port>
</load-balancer>
</server-cluster>
诊断过程:用 Python 检查 DOM 结构
import xml.dom.minidom
xml_str = '''<?xml version="1.0" encoding="UTF-8"?>
<server-cluster>
<server id="web01">
<host>192.168.1.10</host>
<port>8080</port>
<load-balancer id="lb01">
<algorithm>round-robin</algorithm>
<port>80</port>
</load-balancer>
</server-cluster>
'''
try:
dom = xml.dom.minidom.parseString(xml_str)
except Exception as e:
print(f"解析失败: {e}")
# 手动检查文档结构
root = dom.documentElement
print(f"根元素: {root.nodeName}")
print(f"子节点数: {root.childNodes.length}")
for i in range(root.childNodes.length):
child = root.childNodes[i]
if child.nodeType == 1:
print(f" 元素[{i}]: <{child.nodeName}>")
elif child.nodeType == 3:
text = child.nodeValue.strip()
if text:
print(f" 文本[{i}]: '{text}'")
操作结果
诊断发现 <server> 没有关闭标签——缺少 </server>。修复后:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 飞翔科技服务器配置 -->
<server-cluster>
<server id="web01">
<host>192.168.1.10</host>
<port>8080</port>
</server> <!-- ← 补上关闭标签 -->
<load-balancer id="lb01">
<algorithm>round-robin</algorithm>
<port>80</port>
</load-balancer>
</server-cluster>
修复后 DOM 树变为:根元素(server-cluster)→ 两个子元素(server + load-balancer)→ 各自包含子元素和文本节点。大翔总结:"XML 文档=树。学 XML 就是学会在树上走路、查节点、剪叶子。"
易错场景
错误一:两个根元素
<!-- ❌ 一个 XML 文档只能有一个根元素 -->
<books>...</books>
<authors>...</authors>
<!-- ✅ 用统一的根元素包裹 -->
<library>
<books>...</books>
<authors>...</authors>
</library>
错误二:误把属性当子节点
JavaScript/Flex 中常见:试图用 childNodes 获取属性,但属性不在 childNodes 里。必须用 getAttribute() 或 attributes 集合。
错误三:忽略空白文本节点
DOM 中元素之间的换行和缩进也会产生 #text 节点。遍历 childNodes 时要过滤 nodeType == 1(只取元素)或 nodeType == 3 && nodeValue.trim()。
面试考点
| 考点 | 参考答案要点 |
|---|---|
| DOM 有哪几种节点类型? | 七种:元素(1)、属性(2)、文本(3)、CDATA(4)、实体引用(5)、处理指令(7)、注释(8)、文档(9)。最常用的是元素和文本节点 |
| XML 声明是必须的吗? | XML 1.0 可选但推荐,XML 1.1 必需。如果使用 UTF-16 或非默认编码,声明是必要的,否则解析器可能乱码 |
| 如何理解 XML 文档=树? | 从唯一根元素开始,通过父子关系形成层级结构。这种结构天然适合递归遍历,是 XPath、XSLT、DOM 编程的共同基础 |