DTD 教程

DTD 教程

DTD(Document Type Definition,文档类型定义)用于定义 XML 文档的结构规则,声明合法的元素、属性、实体,是 XML 的两种验证方式之一(另一种是 XML Schema)。

DTD 作用

  • 定义结构:规定哪些元素可以出现、顺序与次数
  • 验证文档:确保 XML 符合约定规则
  • 共享标准:多系统间约定数据格式
  • 实体定义:定义可复用内容与外部引用

DTD 与 XML Schema

对比项DTDXML Schema(XSD)
语法非 XMLXML
数据类型有限丰富
命名空间不支持支持
约束能力较弱强(Facet 等)
扩展性低高

DTD 声明方式

# 内部 DTD
<!DOCTYPE 根元素 [
  内部声明
]>

# 外部 DTD
<!DOCTYPE 根元素 SYSTEM "file.dtd">
<!DOCTYPE 根元素 PUBLIC "公共标识" "url.dtd">

学习路线

  1. 简介与构建模块
  2. 元素与属性定义
  3. 实体与验证
  4. 实例与总结

DTD 简介

DTD 简介

1. 什么是 DTD

DTD 是 SGML/XML 的文档类型定义,规定文档的逻辑结构:元素、属性、实体及其关系。合法的 XML 文档必须满足其 DTD 规则。

2. DTD 类型

# 内部 DTD(文档内部声明)
<!DOCTYPE note [
  <!ELEMENT note (to, from, body)>
  <!ELEMENT to (#PCDATA)>
  <!ELEMENT from (#PCDATA)>
  <!ELEMENT body (#PCDATA)>
]>

# 外部 DTD(独立文件)
<!DOCTYPE note SYSTEM "note.dtd">

# 公共 DTD(标准定义)
<!DOCTYPE html PUBLIC
  "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

3. 合法文档示例

<?xml version="1.0"?>
<!DOCTYPE note [
  <!ELEMENT note (to, from, body)>
  <!ELEMENT to (#PCDATA)>
  <!ELEMENT from (#PCDATA)>
  <!ELEMENT body (#PCDATA)>
]>
<note>
  <to>张三</to>
  <from>李四</from>
  <body>你好!</body>
</note>

4. 验证工具

# 命令行验证
xmllint --noout --valid file.xml

# 在线验证
# 各类 XML 验证器

# 编程验证(Python lxml)
from lxml import etree
dtd = etree.DTD("note.dtd")
doc = etree.parse("note.xml")
print(dtd.validate(doc))   # True/False

DTD 构建模块

DTD 构建模块

1. 核心概念

# 元素(Element)
# 文档的基本组成部分
<!ELEMENT 元素名 内容模型>

# 属性(Attribute)
# 元素的附加信息
<!ATTLIST 元素名 属性名 类型 默认值>

# 实体(Entity)
# 可复用的内容或引用
<!ENTITY 实体名 "值">

# PCDATA(可解析字符数据)
# 文本内容,可包含其他元素
# 类似 HTML 的混合内容

# CDATA(字符数据)
# 原始文本,不解析标记

2. 元素内容类型

# 空元素
<!ELEMENT br EMPTY>

# 任意内容
<!ELEMENT any ANY>

# 仅文本
<!ELEMENT title (#PCDATA)>

# 子元素
<!ELEMENT book (title, author, price)>

# 混合内容(文本 + 子元素)
<!ELEMENT p (#PCDATA | b | i)*>

3. 子元素规则

# 必须按顺序
<!ELEMENT book (title, author, price)>

# 选择(任一)
<!ELEMENT color (red | green | blue)>

# 组合
<!ELEMENT family (name, (father | mother), children)>

4. 出现次数

# 次数修饰符
?    出现 0 或 1 次
*    出现 0 或多次
+    出现 1 或多次

# 示例
<!ELEMENT book (title, author?, price*)>
# title 必须 1 次
# author 可选 0-1 次
# price 可 0-多次

# 无修饰符 = 恰好 1 次
<!ELEMENT note (to, from)>

DTD 元素

DTD 元素

1. 声明语法

<!ELEMENT 元素名 内容>

# 内容类型
EMPTY             空元素
ANY               任意内容
(#PCDATA)         文本内容
(子元素列表)       子元素
(混合内容)         文本+子元素

2. 常见声明

# 空元素(如 
、) <!ELEMENT br EMPTY> <!ELEMENT img EMPTY> # 任意内容(宽松) <!ELEMENT note ANY> # 文本元素 <!ELEMENT title (#PCDATA)> # 子元素(有序) <!ELEMENT book (title, author, price)> # 可重复元素 <!ELEMENT library (book+)> <!ELEMENT book (title, author)> # 混合内容 <!ELEMENT para (#PCDATA | b | i | u)*>

3. 完整图书 DTD

<!ELEMENT bookstore (book+)>
<!ELEMENT book (title, author, year?, price)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT year (#PCDATA)>
<!ELEMENT price (#PCDATA)>

# 对应 XML
<bookstore>
  <book>
    <title>XML 入门</title>
    <author>张三</author>
    <year>2026</year>
    <price>59.9</price>
  </book>
</bookstore>

4. 嵌套元素

<!ELEMENT person (name, address)>
<!ELEMENT address (city, street, zip)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT street (#PCDATA)>
<!ELEMENT zip (#PCDATA)>

DTD 属性

DTD 属性

1. 属性声明语法

<!ATTLIST 元素名
  属性名1 类型 默认值
  属性名2 类型 默认值
>

# 示例
<!ATTLIST img
  src CDATA #REQUIRED
  alt CDATA #IMPLIED
  width CDATA #IMPLIED
>

2. 属性类型

CDATA            字符串
ID               唯一标识(不能数字开头)
IDREF            引用其他 ID
IDREFS           多个 ID 引用(空格分隔)
ENTITY           实体引用
ENTITIES         多个实体
NMTOKEN          名称字符
NMTOKENS         多个名称字符
NOTATION         表示法
(枚举值)         取值列表

3. 默认值

#REQUIRED        必须提供
#IMPLIED         可选
#FIXED "值"      固定值(不能修改)
"值"             默认值(可覆盖)

# 示例
<!ATTLIST book
  id CDATA #REQUIRED
  lang CDATA "zh"
  edition CDATA #FIXED "1.0"
  note CDATA #IMPLIED
>

# XML 中使用
<book id="b1" lang="en">...</book>
# id 必须,lang 默认 zh 可改
# edition 固定 1.0,note 可选

4. 枚举属性

# 只能取列表中的值
<!ATTLIST color
  value (red | green | blue) #REQUIRED
>

<!ATTLIST gender
  value (男 | 女) "男"
>

# 用法
<color value="red"/>
<gender value="女"/>

5. 完整属性示例

<!ATTLIST person
  id ID #REQUIRED
  name CDATA #REQUIRED
  email CDATA #IMPLIED
  status (active | inactive) "active"
  manager IDREF #IMPLIED
  skills IDREFS #IMPLIED
>

# XML
<person id="p1" name="张三"
  email="zhang@example.com"
  status="active"
  manager="p2"
  skills="s1 s2 s3">

XML 元素和属性比较

XML 元素和属性比较

1. 两种表达数据的方式

# 用元素存数据
<person>
  <name>张三</name>
  <age>30</age>
</person>

# 用属性存数据
<person name="张三" age="30"/>

# 混合
<person name="张三">
  <age>30</age>
</person>

2. 使用原则

# 建议用元素的场景
- 数据是文档主要内容
- 数据较长(大段文本)
- 数据有复杂结构
- 数据需要扩展子内容

# 建议用属性的场景
- 数据是元信息(标识符)
- 数据是配置(类别/语言)
- 数据是简单枚举
- 数据与元素强相关(修饰)

3. 实践建议

# 行业惯例
# 简单数据: 属性更简洁
# 复杂数据: 元素更可扩展

# DTD 中的选择
# 属性: CDATA/枚举等简单类型
# 元素: 任意结构

# 注意
# 属性值不能包含复杂结构
# 元素可嵌套任意深度

4. 示例对比

# 方式一:全属性
<book title="XML" author="张三"
     price="59.9" year="2026"/>

# 方式二:全元素
<book>
  <title>XML</title>
  <author>张三</author>
  <price>59.9</price>
  <year>2026</year>
</book>

# 方式三:混合(推荐场景)
<book category="技术">
  <title>XML</title>
  <author>张三</author>
</book>

DTD 实体

DTD 实体

1. 什么是实体

实体(Entity)是 DTD 中定义的命名内容,可在文档中引用,用于避免重复、简化维护。

2. 内部实体

# 定义
<!DOCTYPE note [
  <!ENTITY author "张三">
  <!ENTITY copyright "© 2026 知识出版社">
]>

# 使用(&实体名;)
<note>
  <author>&author;</author>
  <copyright>&copyright;</copyright>
</note>

3. 外部实体

# 引用外部文件内容
<!ENTITY logo SYSTEM "logo.xml">
<!ENTITY footer SYSTEM "footer.txt">

# 公共实体
<!ENTITY html PUBLIC
  "-//W3C//DTD XHTML 1.0//EN" "xhtml.dtd">

# 使用
<body>
  &logo;
  ...
</body>

4. 参数实体

# 参数实体:用于 DTD 内部复用(以 % 定义)
<!ENTITY % 通用属性
  "id CDATA #IMPLIED lang CDATA 'zh'">

<!ATTLIST book %通用属性;>
<!ATTLIST author %通用属性;>

# 引用外部 DTD 片段
<!ENTITY % html SYSTEM "html-entities.dtd">
%html;

5. 实体安全注意

# 安全警告:XXE(XML 外部实体注入)
# 恶意 DTD 可读取本地文件
<!ENTITY xxe SYSTEM "file:///etc/passwd">

# 防护
# 解析器禁用外部实体
from lxml import etree
parser = etree.XMLParser(resolve_entities=False)

# Java
DocumentBuilderFactory
  .setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)

DTD 验证

DTD 验证

1. 什么是验证

验证是检查 XML 文档是否符合 DTD 规则的过程。满足规则称为"有效(Valid)",仅格式正确称为"良构(Well-formed)"。

# 良构(Well-formed)
- 标签正确闭合
- 属性加引号
- 正确嵌套
- 单一根元素

# 有效(Valid)
- 良构
- 符合 DTD 规则(元素/属性/顺序)

2. 验证工具

# xmllint
xmllint --noout --valid note.xml

# Python(lxml)
from lxml import etree

# 方式一:DTD 文件
dtd = etree.DTD("note.dtd")
doc = etree.parse("note.xml")
print("有效:", dtd.validate(doc))
print("错误:", dtd.error_log.last_error)

# 方式二:内联 DTD(自动验证)
doc = etree.parse("note.xml")
print(doc.docinfo.doctype)

# Java
import javax.xml.parsers.*;
DocumentBuilderFactory factory =
    DocumentBuilderFactory.newInstance();
factory.setValidating(true);

3. 常见验证错误

# 1. 元素缺失/多余
# 预期 (title, author) 实际只有 title
# 错误: Element 'book' content does not follow the DTD

# 2. 元素顺序错误
# 预期 (title, author) 实际 (author, title)

# 3. 属性缺失
# 预期 id CDATA #REQUIRED 但未提供

# 4. 枚举值不合法
# 预期 (red|green) 实际 blue

4. 验证策略

# 何时用 DTD
- 简单结构定义
- 旧系统兼容
- 教学/简单配置

# 何时用 XSD
- 需要强类型
- 需要命名空间
- 复杂约束

# 何时不用验证
- 内部使用的宽松 XML
- 性能敏感场景(验证有开销)

DTD 实例

DTD 实例

1. 实例:员工信息

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE company [
  <!ELEMENT company (employee+)>
  <!ELEMENT employee (name, department, salary?, email?)>
  <!ELEMENT name (#PCDATA)>
  <!ELEMENT department (#PCDATA)>
  <!ELEMENT salary (#PCDATA)>
  <!ELEMENT email (#PCDATA)>
  <!ATTLIST employee
    id ID #REQUIRED
    status (active | inactive) "active"
  >
]>
<company>
  <employee id="e1" status="active">
    <name>张三</name>
    <department>技术部</department>
    <salary>15000</salary>
    <email>zhang@example.com</email>
  </employee>
  <employee id="e2">
    <name>李四</name>
    <department>市场部</department>
  </employee>
</company>

2. 实例:配置管理

<!DOCTYPE config [
  <!ELEMENT config (app, database, server*)>
  <!ELEMENT app (name, version, debug)>
  <!ELEMENT name (#PCDATA)>
  <!ELEMENT version (#PCDATA)>
  <!ELEMENT debug (#PCDATA)>
  <!ELEMENT database (host, port, user)>
  <!ELEMENT host (#PCDATA)>
  <!ELEMENT port (#PCDATA)>
  <!ELEMENT user (#PCDATA)>
  <!ELEMENT server (ip, role)>
  <!ELEMENT ip (#PCDATA)>
  <!ELEMENT role (#PCDATA)>
  <!ATTLIST server type (web | db | cache) "web">
]>

3. 实例:订单数据

<!DOCTYPE order [
  <!ELEMENT order (customer, items)>
  <!ELEMENT customer (name, address?)>
  <!ELEMENT address (city, street)>
  <!ELEMENT city (#PCDATA)>
  <!ELEMENT street (#PCDATA)>
  <!ELEMENT items (item+)>
  <!ELEMENT item (product, qty, price)>
  <!ELEMENT product (#PCDATA)>
  <!ELEMENT qty (#PCDATA)>
  <!ELEMENT price (#PCDATA)>
  <!ATTLIST order id CDATA #REQUIRED date CDATA #IMPLIED>
]>

DTD 总结

DTD 总结

1. 核心要点

  • 元素:<!ELEMENT> 定义文档结构
  • 属性:<!ATTLIST> 定义元素附加信息
  • 实体:<!ENTITY> 定义可复用内容
  • 验证:确保 XML 符合定义的规则

2. 声明速查

<!ELEMENT 名 (内容)>
  EMPTY / ANY / (#PCDATA) / (子元素) / (混合)
  修饰符: ? 0-1次  * 0-多次  + 1-多次

<!ATTLIST 元素 属性 类型 默认>
  类型: CDATA ID IDREF (枚举)...
  默认: #REQUIRED #IMPLIED #FIXED "值" "值"

<!ENTITY 名 "值">
  使用: &名;
  参数实体: %名;

3. DTD 的优缺点

# 优点
- 语法简单,学习成本低
- 广泛支持(所有 XML 解析器)
- 适合简单结构定义

# 缺点
- 非 XML 语法(工具支持差)
- 无数据类型(全是文本)
- 不支持命名空间
- 约束能力弱
- 不可编程扩展

4. 何时使用

  • 简单配置文档
  • 旧系统/标准(XHTML、RSS 1.0)
  • 需要轻量验证的场景
  • 新项目推荐 XML Schema 或 JSON Schema

5. 进阶方向

# 从 DTD 升级
- XML Schema(XSD): 类型化、命名空间
- RELAX NG: 简洁强大的模式语言
- JSON Schema: JSON 数据验证
- Schematron: 断言式规则

# 现代实践
# 大多数新项目用 JSON + JSON Schema
# XML 主要用于文档与遗留系统
第 1 / 10 节