XML 语法规则
本章定位 :掌握 XML 的五项铁律语法规则——违反任何一条都会导致 XML 解析器报 fatal error 并拒绝处理整个文档。这是 XML 与 HTML 最大的行为差异。
定义与作用
XML 的语法规则是其最核心的约束。与 HTML 的"尽力渲染"策略不同,XML 采用"要么全对,要么全错"的严格策略——任何语法错误都会导致解析器立即停止并报 fatal error 。这种设计哲学源于 XML 的第一个设计目标:"处理 XML 的程序应易于编写"——严格的语法让解析器无需处理各种容错分支,代码更简单可靠。
这套语法规则的另一个重要作用是 保证数据完整性 。在系统间传输数据时,如果接收方收到的 XML 格式良好,就可以放心解析;如果格式有误,就会明确报错而非默默吞掉数据。
核心原理:XML 解析器的处理流程
图解释 :XML 解析器按顺序检查五项规则。任何一项失败都直接报 fatal error。这与 HTML 浏览器的容错机制形成鲜明对比——HTML 即使嵌套错误也试图渲染,XML 直接拒绝处理。
语法/结构要点
五项核心语法规则
| 序号 | 规则 | 正确示例 | 错误示例 |
|---|---|---|---|
| 1 | 所有元素必须有关闭标签 | <p>文本</p> 或 <br/> | <p>文本 |
| 2 | 标签大小写敏感 | <Note>...</Note> | <Note>...</note> |
| 3 | 元素必须正确嵌套 | <b><i>文本</i></b> | <b><i>文本</b></i> |
| 4 | 属性值必须加引号 | <note date="2024/01/01"> | <note date=2024/01/01> |
| 5 | 必须有且只有一个根元素 | <root>...</root> 包裹所有 | 两个顶层元素并排 |
5 个预定义实体引用
XML 中 < 和 & 是保留字符,必须用实体引用转义:
| 字符 | 实体引用 | 说明 |
|---|---|---|
< | < | 小于号(less than) |
> | > | 大于号(greater than) |
& | & | 与符号(ampersand) |
' | ' | 单引号(apostrophe) |
" | " | 双引号(quotation) |
CDATA 段
当文本包含大量特殊字符(如代码片段),逐个转义很繁琐。<![CDATA[...]]> 内的文本被解析器 原样保留 :
<code>
<![CDATA[
if (a < b && b > c) {
System.out.println("a < b < c is false");
}
]]>
</code>
CDATA 段的规则:
- 不能包含
]]>(这表示 CDATA 结束) - 不能嵌套 CDATA 段
- CDATA 内的
<不会被解析为<——所有内容都是字面量
完整示例:小崔发现"幽灵数据"事件
场景说明
飞翔科技技术部的后端开发 小崔 最近接手了实习生写的一个 XML 配置文件。程序在服务器上运行时一切正常,但在他的开发机上解析报错。小崔百思不得其解——"同样的 XML 文件,为什么线上可以,本地不行?"
操作前:有问题的 XML
<?xml version="1.0" encoding="UTF-8"?>
<servers>
<server id="web01">
<name>乐途官网站点</name>
<ip>192.168.1.100</ip>
<port>8080</port>
<server id="db01">
<name>主数据库</name>
<ip>192.168.1.200</ip>
<port>3306</PORT>
</server>
</servers>
问题分析 :
- 第一个
<server>没有关闭标签 (规则1) - 第二个
<port>标签为</PORT>,大小写不匹配(规则2)
应用正确语法后
<?xml version="1.0" encoding="UTF-8"?>
<servers>
<server id="web01">
<name>乐途官网站点</name>
<ip>192.168.1.100</ip>
<port>8080</port>
</server>
<server id="db01">
<name>主数据库</name>
<ip>192.168.1.200</ip>
<port>3306</port>
</server>
</servers>
操作结果
当小崔改正了两处语法错误后,XML 解析顺利通过。后来他发现线上之所以"正常",是因为运维 大翔 在部署前手动用 xmllint 检查并修复了文件——只是没把修复后的版本同步回开发仓库。这导致了一个典型的"线上正常、本地报错"的配置漂移问题。
小崔总结了两条教训:
- XML 的严格语法是好事——如果语法错了但程序"正常"运行,那一定是程序在静默吞掉错误
- 配置文件必须纳入版本控制,不能靠人工"修完就用"
易错场景
错误一:在属性值中使用未转义的双引号
<!-- ❌ 属性值中的双引号会提前结束属性 -->
<book title="He said "Hello" to me">
<!-- ✅ 使用单引号包裹,或实体引用 -->
<book title='He said "Hello" to me'>
<book title="He said "Hello" to me">
错误二:注释中包含双连字符
<!-- ❌ 注释中不能包含 -- -->
<!-- 这是一个 -- 不好的注释 -->
<!-- ✅ 避免连续的两个短横线 -->
<!-- 这是一个正确的注释 -->
错误三:CDATA 段中误放 ]]>
<!-- ❌ CDATA 结束符出现在内容中 -->
<script>
<![CDATA[
if (str.endsWith("]]>")) { } <!-- 解析器会在这里提前结束 CDATA -->
]]>
</script>
<!-- ✅ 拆分 CDATA 段 -->
<script>
<![CDATA[
if (str.endsWith("]]") + ">") { }
]]>
</script>
面试考点
| 考点 | 参考答案要点 |
|---|---|
| 为什么 XML 采用 fatal error 而非容错策略? | XML 是数据交换格式而非显示格式——错误的数据比没有数据更危险。严格的语法让解析器实现简单(W3C 设计目标第4条),也让数据接收方可以信赖解析结果 |
| 什么时候用 CDATA 段,什么时候用实体引用? | 少量特殊字符用实体引用(<),大段代码/脚本用 CDATA。CDATA 不能嵌套,内容不能含 ]]> |
XML 声明 <?xml version="1.0" encoding="UTF-8"?> 是必须的吗? | XML 1.0 中可选但强烈推荐;XML 1.1 中必需。encoding 默认 UTF-8。如果文档以 UTF-16 编码且没有声明,解析器可能无法正确识别 |