DTD 元素声明
本章定位 :掌握 DTD 中的元素声明语法——四种内容模型(EMPTY、ANY、#PCDATA、子元素)、出现次数修饰符(+ / * / ?)和选择符(|)的精确语义。
定义与作用
DTD 的 <!ELEMENT> 声明定义了 XML 文档中每个元素的 合法内容模型——即该元素内部可以出现什么(纯文本、子元素、混合或为空)。这是 DTD 验证的核心部分。
DTD 元素声明的本质是用形式化语法描述文档结构。解析器读取 DTD 后,就会按照声明逐一验证每个元素实例是否符合定义。
核心原理:内容模型的验证流程
图解释 :DTD 验证器读取元素声明后,根据内容模型类型进入不同的验证分支。子元素模型最复杂——验证器需要按顺序逐个子元素检查,并根据出现次数修饰符判断是否接受。
语法/结构要点
四种内容模型
| 模型 | 声明语法 | 允许的内容 | 示例 |
|---|---|---|---|
| EMPTY | <!ELEMENT br EMPTY> | 不能有任何内容 | <br/> |
| ANY | <!ELEMENT data ANY> | 任何可解析内容 | 不推荐 |
| #PCDATA | <!ELEMENT title (#PCDATA)> | 纯文本 | <title>XML</title> |
| 子元素 | <!ELEMENT book (title, author, year)> | 按序排列的子元素 | 见下方示例 |
出现次数修饰符
| 修饰符 | 含义 | 示例 | 匹配 |
|---|---|---|---|
| 无修饰 | 恰好出现 1 次 | (title) | 1 个 title |
+ | 出现 1 次或多次 | (author+) | 至少 1 个 author |
* | 出现 0 次或多次 | (note*) | 0 个或多个 note |
? | 出现 0 次或 1 次 | (discount?) | 0 或 1 个 discount |
选择符 | 和括号组合
<!-- book 包含 1 个 title,然后是 1 个或多个 (author 或 editor) -->
<!ELEMENT book (title, (author | editor)+)>
<!-- person 包含 name,然后可选 phone 或 email -->
<!ELEMENT person (name, (phone | email)?)>
完整示例:白歌设计飞翔科技图书 DTD
场景说明
飞翔科技架构师 白歌 正在设计公司图书管理系统的 XML Schema。她先用 DTD 建立原型,然后再考虑迁移到更强大的 XSD。她需要为图书目录设计元素声明。
操作前:口头约定,无规范
团队开发时各人按自己的理解写 XML,导致 3 个人写出了 3 种结构变体。这直接导致解析代码需要处理大量 if-else。
操作后:白歌写的完整元素声明 DTD
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookstore [
<!ELEMENT bookstore (book+)>
<!-- book:title 必填,author 至少1个,year 必填,price 必填,
review 可选多个,award 可选多个 -->
<!ELEMENT book (title, author+, year, price, review*, award*)>
<!-- 简单文本元素 -->
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT year (#PCDATA)>
<!ELEMENT price (#PCDATA)>
<!-- review 包含 text 和 rating(选其一) -->
<!ELEMENT review (text | rating)>
<!ELEMENT text (#PCDATA)>
<!ELEMENT rating (#PCDATA)>
<!-- award 纯文本 + 空元素可选 -->
<!ELEMENT award (#PCDATA)>
]>
<bookstore>
<book>
<title>Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
<review><text>Excellent introduction to XML.</text></review>
<review><rating>5</rating></review>
<award>Best Tech Book 2003</award>
</book>
</bookstore>
操作结果
现在所有开发者必须遵循这段 DTD 定义的规则:
- 只有
review元素可以包含text或rating(但不能同时包含两者) author至少 1 个,review可以 0 或多个- 元素出现顺序固定:
title → author → year → price → review → award
如果有新人加入团队,白歌只需要让他看 DTD 文件,结构规则一目了然。
易错场景
错误一:混淆 + 和 *
<!-- 要求至少 1 个 author —— 如果 XML 中没有 author,验证失败 -->
<!ELEMENT book (title, author+, year)>
<!-- 允许 0 个 author —— author 完全是可选的 -->
<!ELEMENT book (title, author*, year)>
错误二:忘记括号
<!-- ❌ 语法错误 -->
<!ELEMENT book title, author>
<!-- ✅ 子元素列表必须加括号 -->
<!ELEMENT book (title, author)>
错误三:ANY 的陷阱
<!-- ❌ ANY 意味着任何内容都能通过验证,完全失去约束力 -->
<!ELEMENT data ANY>
ANY 在原型阶段可能有用,但生产环境不应使用——它让 DTD 形同虚设。
面试考点
| 考点 | 参考答案要点 |
|---|---|
| DTD 中有哪几种内容模型? | 四种:EMPTY(空元素)、ANY(任意内容)、#PCDATA(纯文本)、子元素列表(复合元素) |
+、*、? 的语义分别是什么? | + 表示至少 1 次(1 到 n),* 表示 0 到 n 次,? 表示 0 或 1 次。无修饰符表示恰好 1 次 |
| 为什么生产环境不推荐 ANY? | ANY 允许任意内容,不提供任何结构约束,等于没有验证。它只在开发初期快速原型时有用 |