DTD全称为文档类型定义,是XML技术体系里用于定义XML文档合法结构的规范工具,它可以明确指定一个XML文档中能够包含哪些元素、每个元素可以拥有哪些属性、元素之间的嵌套规则以及可以使用的实体等内容,让XML文档的结构符合统一的标准,避免解析时出现结构错误。
DTD的核心作用
DTD的存在主要是为了解决XML文档的结构规范性问题,它的核心作用主要有以下几个:
- 约束XML文档的元素组成,规定哪些元素是必须存在的,哪些是可选存在的
- 定义元素之间的嵌套关系,明确父元素可以包含哪些子元素,子元素的出现顺序和次数
- 定义元素的属性规则,包括属性的名称、类型、是否必填以及默认值
- 定义可重用的实体,减少XML文档中的重复内容编写
DTD的两种声明方式
DTD可以分为内部DTD和外部DTD两种声明方式,开发者可以根据实际使用场景选择。
内部DTD
内部DTD是直接写在XML文档内部的DTD定义,它的声明语法格式如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 根元素名 [
<!ELEMENT 元素名 内容规则>
<!ATTLIST 元素名 属性名 属性类型 属性设置>
]>
下面是一个内部DTD的实际示例,定义了一个学生信息XML的合法结构:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE students [
<!-- 定义根元素students,可以包含1到多个student子元素 -->
<!ELEMENT students (student+)>
<!-- 定义student元素,包含id、name、age三个子元素,按顺序出现 -->
<!ELEMENT student (id, name, age)>
<!-- 定义id元素,内容为字符数据 -->
<!ELEMENT id (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!-- 定义student元素的属性,sid为必填的字符串类型属性 -->
<!ATTLIST student sid CDATA #REQUIRED>
]>
<students>
<student sid="s001">
<id>1</id>
<name>张三</name>
<age>18</age>
</student>
<student sid="s002">
<id>2</id>
<name>李四</name>
<age>19</age>
</student>
</students>
外部DTD
外部DTD是将DTD定义单独放在一个以.dtd为后缀的文件中,然后在XML文档中引入该文件,适合多个XML文档共用同一套结构规则的场景。外部DTD又分为私有外部DTD和公共外部DTD,这里主要介绍常用的私有外部DTD。
外部DTD文件的示例(student.dtd):
<!-- 学生信息DTD定义文件 --> <!ELEMENT students (student+)> <!ELEMENT student (id, name, age)> <!ELEMENT id (#PCDATA)> <!ELEMENT name (#PCDATA)> <!ELEMENT age (#PCDATA)> <!ATTLIST student sid CDATA #REQUIRED>
XML文档中引入外部DTD的语法如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE 根元素名 SYSTEM "dtd文件路径">
引入上面student.dtd的XML文档示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE students SYSTEM "student.dtd">
<students>
<student sid="s003">
<id>3</id>
<name>王五</name>
<age>20</age>
</student>
</students>
DTD定义元素和属性的规则
元素定义规则
使用<!ELEMENT>关键字定义元素,基本语法为<!ELEMENT 元素名 内容规则>,内容规则的常见类型如下:
| 内容规则 | 含义 | 示例 |
|---|---|---|
| #PCDATA | 元素内容为字符数据,不能包含子元素 | <!ELEMENT name (#PCDATA)> |
| 子元素列表 | 元素包含指定的子元素,用逗号分隔表示顺序出现 | <!ELEMENT student (id, name)> |
| 子元素|分隔 | 元素包含的子元素任选其一 | <!ELEMENT info (phone|email)> |
| 出现次数修饰符 | +表示1次或多次,*表示0次或多次,?表示0次或1次 | <!ELEMENT students (student+)> |
| EMPTY | 元素为空元素,没有内容也没有子元素 | <!ELEMENT br EMPTY> |
| ANY | 元素可以包含任意类型的内容,不建议使用 | <!ELEMENT note ANY> |
属性定义规则
使用<!ATTLIST>关键字定义元素的属性,基本语法为<!ATTLIST 元素名 属性名 属性类型 属性设置>,常见的属性类型如下:
- CDATA:属性值为字符数据
- ID:属性值为唯一标识,同一个XML文档中ID属性值不能重复
- IDREF:属性值为另一个元素的ID属性值,用于建立元素间的引用关系
- 枚举类型:属性值只能从指定的几个值中选择,用竖线分隔,比如(男|女)
属性设置的常见取值:
- #REQUIRED:属性为必填项
- #IMPLIED:属性为可选项
- #FIXED "值":属性为固定值,XML中不能修改
- 默认值:如果不写该属性,默认使用指定的默认值
下面是一个包含多种属性类型的示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE users [
<!ELEMENT users (user+)>
<!ELEMENT user (name, gender)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT gender (#PCDATA)>
<!ATTLIST user
uid ID #REQUIRED
ref_id IDREF #IMPLIED
role (admin|user) "user"
status CDATA #FIXED "active"
>
]>
<users>
<user uid="u1" role="admin">
<name>管理员</name>
<gender>男</gender>
</user>
<user uid="u2" ref_id="u1">
<name>普通用户</name>
<gender>女</gender>
</user>
</users>
DTD定义实体
实体是可以被引用的占位符,分为内部实体和外部实体,使用<!ENTITY>关键字定义,基本语法为<!ENTITY 实体名 "实体内容">,在XML文档中使用&实体名;来引用实体。
内部实体示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY author "张三">
<!ELEMENT note (content)>
<!ELEMENT content (#PCDATA)>
]>
<note>
<content>本文作者是&author;</content>
</note>
解析后content元素的内容会变为本文作者是张三。
DTD的局限性
虽然DTD可以定义XML的合法结构,但它也存在一些局限性:
- DTD不是使用XML语法编写,学习成本相对较高
- DTD对数据类型的约束比较弱,只支持字符类型,无法约束数字、日期等具体类型
- DTD不支持命名空间,不适合复杂的XML应用场景
如果需要更强大的XML结构约束能力,可以考虑使用XML Schema(XSD),它弥补了DTD的很多不足,是目前更主流的XML约束规范。