XML文档类型定义也就是DOCTYPE声明,是XML文档头部的重要配置内容,它用来指定当前XML文档所遵循的文档类型定义规则,帮助解析器校验文档结构的合法性。如果DOCTYPE声明丢失或者存在语法错误,解析器可能无法正确识别文档结构,进而抛出解析异常。

DOCTYPE声明的基本语法
DOCTYPE声明必须位于XML文档的最顶部,在XML声明之后,根元素之前,基本语法格式如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 根元素名称 SYSTEM "外部DTD文件路径">
<根元素名称>
<子元素>内容</子元素>
</根元素名称>
如果是使用内部DTD定义,语法格式为:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 根元素名称 [
<!ELEMENT 根元素名称 (子元素+)>
<!ELEMENT 子元素 (#PCDATA)>
]>
<根元素名称>
<子元素>内容</子元素>
</根元素名称>
语法要点说明
- DOCTYPE关键词必须大写,后面的根元素名称要和XML文档的实际根元素完全一致
- 使用SYSTEM关键字时,后面跟随的是外部DTD文件的URI路径,可以是本地路径也可以是网络路径
- 如果使用内部DTD,定义内容要放在方括号[]内部,每个元素、属性定义都要符合DTD语法规则
- 整个DOCTYPE声明以>结尾,中间不能出现换行导致语法断裂
DOCTYPE声明的核心作用
1. 文档结构校验
DOCTYPE关联DTD之后,XML解析器会按照DTD中定义的规则校验文档结构,比如根元素下允许出现哪些子元素、子元素的出现次数、元素是否允许有属性等,不符合规则的文档会被判定为无效。
2. 实体定义支持
DTD中可以定义实体,比如预定义实体或者自定义实体,DOCTYPE声明让解析器能够识别这些实体,在解析文档时正确替换实体内容。例如内部DTD中定义<!ENTITY author "张三">,后续文档中可以使用&author;来引用这个实体。
3. 保证文档一致性
多个XML文档如果关联同一个DTD,就能保证这些文档的结构、元素规则一致,方便数据的交换和处理,减少不同系统之间解析XML时的兼容性问题。
常见的DOCTYPE错误场景
场景1:DOCTYPE声明丢失
如果XML文档没有写DOCTYPE声明,但是解析器开启了DTD校验模式,就会抛出校验异常,比如Java中使用DOM解析器开启校验时的报错信息会提示找不到文档类型定义。
错误示例:
<?xml version="1.0" encoding="UTF-8"?>
<user>
<name>李四</name>
<age>20</age>
</user>
如果解析要求校验user元素的结构,缺少DOCTYPE声明就会报错,修正后添加对应的DOCTYPE即可。
场景2:根元素名称不匹配
DOCTYPE中写的根元素名称和实际XML根元素不一致,会导致解析失败。
错误示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE users SYSTEM "user.dtd">
<user>
<name>李四</name>
</user>
这里DOCTYPE定义的根元素是users,实际根元素是user,两者不匹配,修正时把DOCTYPE中的根元素改成user即可。
场景3:外部DTD路径错误
使用SYSTEM关键字指定外部DTD时,如果路径不存在或者无法访问,解析器会提示找不到DTD文件,需要检查路径是否正确,或者将DTD文件放到正确的位置。
实际使用示例
下面是一个完整的内部DTD的XML示例,DOCTYPE声明语法正确,结构符合DTD定义:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book [
<!ELEMENT book (title, author, price)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT price (#PCDATA)>
<!ATTLIST book id CDATA #REQUIRED>
]>
<book id="b001">
<title>XML开发指南</title>
<author>王五</author>
<price>59.9</price>
</book>
这个示例中,DOCTYPE定义了book根元素下必须包含title、author、price三个子元素,book元素有一个必填的id属性,文档内容完全符合这个规则,解析时不会出现问题。