文档类型定义(DTD)可定义合法的XML文档构建模块,它使用一系列合法的元素来定义文档的结构。可以看做是XML文档的数据结构,可以形成一种可靠的错误监测机制,程序员或解析器可以由此查找可能的错误。如果不指定DTD,程序可以运行(XML可以被解析),但是你无法确定XML中的数据是完全符合要求的。有时候,不正确的数据会导致莫名其妙的错误。
具体的可参考DTD教程。
如下实例:
1 <?xml version="1.0"?> 2 <!DOCTYPE note [ 3 <!ELEMENT note (to,from,heading,body)> 4 <!ELEMENT to (#PCDATA)> 5 <!ELEMENT from (#PCDATA)> 6 <!ELEMENT heading (#PCDATA)> 7 <!ELEMENT body (#PCDATA)> 8 ]> 9 <note> 10 <to>George</to> 11 <from>John</from> 12 <heading>Reminder</heading> 13 <body>Don‘t forget the meeting!</body> 14 </note>
以上 DTD 解释如下:
!DOCTYPE note (第二行)定义此文档是 note 类型的文档。
!ELEMENT note (第三行)定义 note 元素有四个元素:"to、from、heading、body"
!ELEMENT to (第四行)定义 to 元素为 "#PCDATA" 类型
!ELEMENT from (第五行)定义 from 元素为 "#PCDATA" 类型
!ELEMENT heading (第六行)定义 heading 元素为 "#PCDATA" 类型
!ELEMENT body (第七行)定义 body 元素为 "#PCDATA" 类型
注意:当子元素按照由逗号分隔开的序列进行声明时,这些子元素必须按照相同的顺序出现在文档中。在一个完整的声明中,子元素也必须被声明,同时子元素也可拥有子元素。
元素的数据类型有:
#CDATA:(Character Data)指元素包含不通过解析器解析的字符数据。特殊字符和保留字不需要转义。
#PCDATA: (Parsed CDATA)指元素包括解析器可解析字符数据。特殊字符和保留字需要转义才可以通过解析器。
ANY:元素可以包含任何声明类型的子元素和字符数据。
如果是空元素如<note></note>或者<note/>则定义如下:
<!ELEMENT note EMPTY>
假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中,如下所示:
1 <?xml version="1.0"?> 2 <!DOCTYPE note SYSTEM "note.dtd"> 3 <note> 4 <to>George</to> 5 <from>John</from> 6 <heading>Reminder</heading> 7 <body>Don‘t forget the meeting!</body> 8 </note>
这是包含 DTD 的 "note.dtd" 文件:
1 <!ELEMENT note (to,from,heading,body)> 2 <!ELEMENT to (#PCDATA)> 3 <!ELEMENT from (#PCDATA)> 4 <!ELEMENT heading (#PCDATA)> 5 <!ELEMENT body (#PCDATA)>
DTD声明中还可以控制元素出现的次数及多个选项,如下所示:
<!ELEMENT team (people+,name*,gender?,(message|body))>
team表示只出现1次,people+表示至少出现1次,name*表示出现0次或者多次,gender?表示出现0次或者1次,(message|body)表示message和body元素任选1个。
除了可以声明元素外,还可以声明属性,格式为:<!ATTLIST 元素名称 属性名称 属性类型 默认值>
如下所示:
1 <?xml version="1.0"?> 2 <!DOCTYPE team [ 3 <!ELEMENT team (people+)> 4 <!ELEMENT people (name,gender)> 5 <!ELEMENT name (#CDATA)> 6 <!ELEMENT gender (#CDATA)> 7 <!ATTLIST people type CDATA "employee"> 8 ]> 9 10 <team> 11 <people type="employee"> 12 <name>zhangsan</name> 13 <gender>f</gender> 14 </people> 15 <people type="leader"> 16 <name>lisi</name> 17 <gender>m</gender> 18 </people> 19 </team>
其中属性类型的选项如下:
类型 | 描述 |
CDATA | 值为字符数据 |
(en1|en2|...) | 此值是枚举列表中的一个值 |
ID | 值为唯一的id |
IDREF | 值为另外一个元素的id |
IDREFS | 值为其他id的列表 |
NMTOKEN | 值为合法的 XML 名称 |
NMTOLENS | 值为合法的 XML 名称的列表 |
ENTITY | 值为一个实体 |
ENTITIES | 值为一个实体列表 |
NOTATION | 此值是符号的名称 |
xml: | 值是一个预定义的xml值 |
默认值参数可使用下列值:
值 | 描述 |
值 | 属性的默认值 |
#REQUIRED | 属性值是必需的 |
#IMPLIED | 属性不是必需的 |
#FIXED value | 属性值是固定的 |
外部实体引用的例子如下所示:
people1.xml
<?xml version="1.0"?> <people id="1"> <name>zhangsan</name> <gender>f</gender> </people>
people2.xml
1 <?xml version="1.0"?> 2 <people id="2"> 3 <name>lisi</name> 4 <gender>m</gender> 5 </people>
team.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE team [ 3 <!ENTITY people1 SYSTEM "./people1.xml"> 4 <!ENTITY people2 SYSTEM "./people2.xml"> 5 ]> 6 <team> 7 &people1; 8 &people2; 9 </team>
最后形成的xml文档如下:
1 <?xml version="1.0"?> 2 <team> 3 <people id="1"> 4 <name>zhangsan</name> 5 <gender>m</gender> 6 </people> 7 <people id="2"> 8 <name>lisi</name> 9 <gender>f</gender> 10 </people> 11 </team>
注意:由于外部实体存在注入漏洞,因为xml解析器会以当前上下文的环境引用外部资源作为实体的内容,从而在实际的应用上下文里将该部分数据引入逻辑流程进行处理。所以目前有些解析器不会解析外部实体。最好检查所使用的底层xml解析库,默认禁止外部实体的解析,同时增强对系统的监控,防止此问题被人利用。