1. 什么是DTD

DTD(Document Type Definition,文档类型定义)是一种文档类型定义语言,用于定义XML文档的结构、元素和属性。DTD为XML文档提供了一套规则,规定了文档中可以包含哪些元素、元素之间的关系、元素可以有哪些属性以及它们的默认值等。

DTD最早是为SGML(Standard Generalized Markup Language)设计的,后来被XML(eXtensible Markup Language)所采用。通过DTD,可以确保XML文档的结构符合预定义的规范,从而实现数据的标准化和互操作性。

1.1 DTD的作用

DTD在XML文档中扮演着重要的角色,主要作用包括:

  • 结构验证:确保XML文档遵循预定义的结构规则
  • 数据完整性:通过定义元素和属性约束,保证数据的完整性和一致性
  • 文档标准化:为XML文档提供统一的标准,便于不同系统之间的数据交换
  • 提高可读性:明确的文档结构定义使XML文档更易于理解和维护

2. DTD的基本语法

DTD可以使用两种方式包含在XML文档中:内部DTD和外部DTD。

2.1 内部DTD

内部DTD直接包含在XML文档的DOCTYPE声明中。基本语法如下:

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

例如,一个简单的内部DTD示例:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE note [ <!ELEMENT note (to, from, heading, body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]> <note> <to>John</to> <from>Jane</from> <heading>Reminder</heading> <body>Don't forget the meeting tomorrow!</body> </note> 

2.2 外部DTD

外部DTD存储在单独的文件中,通常以.dtd为扩展名。XML文档通过DOCTYPE声明引用外部DTD文件。

引用外部DTD的语法:

<!DOCTYPE 根元素 SYSTEM "DTD文件路径"> 

例如,假设有一个名为note.dtd的外部DTD文件:

<!ELEMENT note (to, from, heading, body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> 

XML文档可以这样引用它:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <to>John</to> <from>Jane</from> <heading>Reminder</heading> <body>Don't forget the meeting tomorrow!</body> </note> 

还可以使用公共标识符引用DTD:

<!DOCTYPE 根元素 PUBLIC "公共标识符" "DTD文件路径"> 

例如:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

3. 元素声明

在DTD中,元素声明是最基本的组成部分,用于定义XML文档中可以包含的元素及其内容模型。

3.1 基本元素声明语法

元素声明的基本语法如下:

<!ELEMENT 元素名称 内容模型> 

3.2 内容模型类型

DTD中定义了多种内容模型类型,用于指定元素可以包含的内容:

3.2.1 空元素

空元素不包含任何内容,使用关键字EMPTY声明:

<!ELEMENT img EMPTY> 

在XML文档中,空元素可以表示为:

<img src="image.jpg" /> 

3.2.2 只包含文本的元素

只包含文本的元素使用关键字#PCDATA(Parsed Character Data)声明:

<!ELEMENT title (#PCDATA)> 

在XML文档中:

<title>DTD Tutorial</title> 

3.2.3 包含子元素的元素

包含子元素的元素通过列出子元素的名称来声明:

<!ELEMENT book (title, author, publisher, price)> 

这表示book元素必须按顺序包含title、author、publisher和price四个子元素。

3.2.4 混合内容元素

混合内容元素可以包含文本和子元素,使用混合内容声明:

<!ELEMENT description (#PCDATA | emph | strong)*> 

这表示description元素可以包含文本、emph元素和strong元素,它们可以按任意顺序出现零次或多次。

3.2.5 任意内容元素

使用关键字ANY声明的元素可以包含任何内容:

<!ELEMENT misc ANY> 

在XML文档中:

<misc> This is some text <b>with bold</b> and <i>italic</i> text. </misc> 

3.3 元素出现次数指示符

在元素声明中,可以使用特殊字符来指示子元素的出现次数:

  • ? - 出现零次或一次
  • * - 出现零次或多次
  • + - 出现一次或多次

例如:

<!ELEMENT book (title, author+, publisher?, chapter*)> 

这表示:

  • book元素必须包含一个title元素
  • 必须包含一个或多个author元素
  • 可以包含零个或一个publisher元素
  • 可以包含零个或多个chapter元素

3.4 选择列表

使用竖线 | 可以创建选择列表,表示只能从列表中选择一个元素:

<!ELEMENT choice (option1 | option2 | option3)> 

这表示choice元素必须包含option1、option2或option3中的一个。

3.5 组合使用

可以将上述各种方式组合使用,创建复杂的内容模型:

<!ELEMENT book (title, author+, (publisher | publication), chapter*, appendix?)> 

这表示:

  • book元素必须包含一个title元素
  • 必须包含一个或多个author元素
  • 必须包含一个publisher或publication元素(二选一)
  • 可以包含零个或多个chapter元素
  • 可以包含零个或一个appendix元素

4. 属性声明

DTD允许为元素定义属性,属性声明使用ATTLIST关键字。

4.1 属性声明语法

属性声明的基本语法如下:

<!ATTLIST 元素名称 属性名称 属性类型 属性默认值 ... > 

例如:

<!ATTLIST img src CDATA #REQUIRED alt CDATA #IMPLIED width CDATA "100" height CDATA "100" > 

4.2 属性类型

DTD定义了多种属性类型:

4.2.1 CDATA

CDATA(Character Data)类型的属性可以包含任何文本字符:

<!ATTLIST book title CDATA #REQUIRED > 

4.2.2 枚举类型

枚举类型限制属性值必须为预定义的选项之一:

<!ATTLIST payment method (credit|debit|cash) "credit" > 

这表示method属性的值只能是credit、debit或cash,默认值为credit。

4.2.3 ID和IDREF

ID类型的属性值必须是唯一的标识符,IDREF类型的属性值必须引用文档中某个元素的ID属性:

<!ATTLIST employee empID ID #REQUIRED manager IDREF #IMPLIED > 

4.2.4 IDREFS

IDREFS类型的属性值可以包含多个ID引用,用空格分隔:

<!ATTLIST project projectID ID #REQUIRED members IDREFS #IMPLIED > 

4.2.5 NMTOKEN和NMTOKENS

NMTOKEN(Name Token)类型的属性值必须是一个有效的XML名称,NMTOKENS可以包含多个NMTOKEN,用空格分隔:

<!ATTLIST product productCode NMTOKEN #REQUIRED categories NMTOKENS #IMPLIED > 

4.2.6 NOTATION

NOTATION类型的属性值引用一个在DTD中声明的符号:

<!NOTATION GIF SYSTEM "image/gif"> <!NOTATION JPEG SYSTEM "image/jpeg"> <!ATTLIST image src CDATA #REQUIRED type NOTATION (GIF | JPEG) #REQUIRED > 

4.2.7 实体

实体类型的属性值引用一个在DTD中声明的实体:

<!ENTITY logo SYSTEM "logo.gif" NDATA GIF> <!ATTLIST figure image ENTITY #REQUIRED > 

4.3 属性默认值

DTD中定义了四种属性默认值:

4.3.1 #REQUIRED

属性必须提供值:

<!ATTLIST book isbn CDATA #REQUIRED > 

4.3.2 #IMPLIED

属性是可选的:

<!ATTLIST book edition CDATA #IMPLIED > 

4.3.3 #FIXED

属性有固定值,不能更改:

<!ATTLIST book version CDATA #FIXED "1.0" > 

4.3.4 默认值

为属性提供默认值,如果未提供则使用默认值:

<!ATTLIST book language CDATA "en" > 

5. 实体声明

实体是用于定义文本或数据的快捷方式,可以在XML文档中引用。DTD中可以声明多种类型的实体。

5.1 内部通用实体

内部通用实体在DTD内部定义,并在XML文档中使用:

<!ENTITY company "ABC Corporation"> 

在XML文档中引用:

<supplier>&company;</supplier> 

5.2 外部通用实体

外部通用实体引用外部文件:

<!ENTITY footer SYSTEM "footer.xml"> 

在XML文档中引用:

<document> <content>...</content> &footer; </document> 

5.3 参数实体

参数实体只能在DTD内部使用,以%开头:

<!ENTITY % commonElements "title | author | date"> <!ELEMENT book (%commonElements;, publisher, price)> 

5.4 外部参数实体

外部参数实体引用外部DTD文件:

<!ENTITY % commonDTD SYSTEM "common.dtd"> %commonDTD; 

5.5 未解析实体

未解析实体用于引用非XML数据,如图片或二进制文件:

<!NOTATION GIF SYSTEM "image/gif"> <!ENTITY logo SYSTEM "logo.gif" NDATA GIF> 

在XML文档中引用:

<image src="logo"/> 

6. DTD的高级特性

6.1 条件段

DTD支持条件段,可以根据条件包含或排除部分DTD声明:

<![ INCLUDE [ <!ELEMENT note (to, from, heading, body)> ]]> <![ IGNORE [ <!ELEMENT note (message)> ]]> 

6.2 注释

在DTD中可以使用注释:

<!-- 这是一个DTD注释 --> <!ELEMENT note (to, from, heading, body)> 

7. 实例:创建完整的DTD

让我们创建一个完整的DTD示例,用于描述图书信息。

7.1 DTD文件(book.dtd)

<!-- 图书DTD定义 --> <!-- 实体声明 --> <!ENTITY % commonElements "title | author | publisher | isbn | price"> <!-- 元素声明 --> <!ELEMENT bookstore (book+)> <!ELEMENT book (%commonElements;, description?, chapter*)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (name, email?)> <!ELEMENT name (#PCDATA)> <!ELEMENT email (#PCDATA)> <!ELEMENT publisher (#PCDATA)> <!ELEMENT isbn (#PCDATA)> <!ELEMENT price (#PCDATA)> <!ELEMENT description (#PCDATA | emph | strong)*> <!ELEMENT chapter (title, content+)> <!ELEMENT content (#PCDATA | emph | strong)*> <!ELEMENT emph (#PCDATA)> <!ELEMENT strong (#PCDATA)> <!-- 属性声明 --> <!ATTLIST book id ID #REQUIRED category (fiction|non-fiction|technical) "fiction" language CDATA "en" edition CDATA "1" year CDATA #IMPLIED > <!ATTLIST chapter number CDATA #REQUIRED pages CDATA #IMPLIED > <!ATTLIST price currency (USD|EUR|GBP|JPY) "USD" > 

7.2 XML文档(bookstore.xml)

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE bookstore SYSTEM "book.dtd"> <bookstore> <book id="b1" category="fiction" language="en" edition="2" year="2020"> <title>The Great Novel</title> <author> <name>John Smith</name> <email>john@example.com</email> </author> <publisher>ABC Books</publisher> <isbn>123-4567890123</isbn> <price currency="USD">24.99</price> <description>A <emph>captivating</emph> story about <strong>adventure</strong> and discovery.</description> <chapter number="1" pages="25"> <title>The Beginning</title> <content>Once upon a time...</content> <content>In a land far away...</content> </chapter> <chapter number="2" pages="30"> <title>The Journey</title> <content>The hero sets out on a quest...</content> </chapter> </book> <book id="b2" category="technical" language="en"> <title>XML Programming</title> <author> <name>Jane Doe</name> </author> <publisher>Tech Press</publisher> <isbn>987-6543210987</isbn> <price currency="USD">49.99</price> <description>A comprehensive guide to <strong>XML</strong> and related technologies.</description> </book> </bookstore> 

8. 验证XML文档

验证XML文档是否符合DTD定义是确保文档结构正确的重要步骤。有多种工具可以验证XML文档:

8.1 使用浏览器验证

现代浏览器(如Chrome、Firefox)可以加载XML文档并在有错误时显示错误信息。只需在浏览器中打开XML文件即可。

8.2 使用在线验证工具

有许多在线工具可以验证XML文档,如:

  • https://www.xmlvalidation.com/
  • https://www.freeformatter.com/xml-validator-xsd.html

8.3 使用编程语言验证

8.3.1 Java示例

import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import java.io.File; public class DTDValidator { public static void main(String[] args) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); // 启用DTD验证 DocumentBuilder builder = factory.newDocumentBuilder(); // 设置错误处理器 builder.setErrorHandler(new org.xml.sax.ErrorHandler() { public void warning(SAXParseException e) throws SAXException { System.out.println("Warning: " + e.getMessage()); } public void error(SAXParseException e) throws SAXException { System.out.println("Error: " + e.getMessage()); } public void fatalError(SAXParseException e) throws SAXException { System.out.println("Fatal Error: " + e.getMessage()); throw e; } }); // 解析XML文档 builder.parse(new File("bookstore.xml")); System.out.println("XML文档验证通过!"); } catch (Exception e) { System.out.println("XML文档验证失败:" + e.getMessage()); } } } 

8.3.2 Python示例

from xml.dom import minidom from xml.parsers.expat import ExpatError try: # 解析XML文档并验证DTD doc = minidom.parse("bookstore.xml") print("XML文档验证通过!") except ExpatError as e: print(f"XML文档验证失败:{e}") except Exception as e: print(f"发生错误:{e}") 

9. DTD的优缺点

9.1 优点

  1. 简单易学:DTD语法相对简单,容易上手
  2. 广泛支持:几乎所有XML处理器都支持DTD验证
  3. 历史悠久:DTD是XML最早的验证机制,有丰富的使用经验
  4. 定义实体:DTD可以定义实体,提供文本替换功能
  5. 标准化:许多行业标准(如XHTML、SVG)都使用DTD

9.2 缺点

  1. 数据类型有限:DTD只支持有限的数据类型,无法定义更复杂的数据约束
  2. 语法不同:DTD使用不同于XML的语法,增加了学习成本
  3. 命名空间支持有限:DTD对XML命名空间的支持不够完善
  4. 不可扩展:DTD无法进行扩展或继承
  5. 文档结构限制:难以表达复杂的文档结构约束

10. DTD与XML Schema的比较

XML Schema是DTD的现代替代方案,提供了更强大的功能和更丰富的数据类型。以下是DTD与XML Schema的主要区别:

特性DTDXML Schema
语法非XML语法XML语法
数据类型有限丰富,支持自定义
命名空间有限支持完全支持
继承不支持支持
可扩展性有限高度可扩展
文档结构简单复杂但精确
实体定义支持不支持

10.1 XML Schema示例

与之前的book.dtd等效的XML Schema(book.xsd)示例:

<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="bookstore"> <xs:complexType> <xs:sequence> <xs:element name="book" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="email" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="publisher" type="xs:string"/> <xs:element name="isbn" type="xs:string"/> <xs:element name="price" type="xs:decimal"/> <xs:element name="description" minOccurs="0"> <xs:complexType mixed="true"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="emph" type="xs:string"/> <xs:element name="strong" type="xs:string"/> </xs:choice> </xs:complexType> </xs:element> <xs:element name="chapter" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="content" maxOccurs="unbounded"> <xs:complexType mixed="true"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="emph" type="xs:string"/> <xs:element name="strong" type="xs:string"/> </xs:choice> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="number" type="xs:string" use="required"/> <xs:attribute name="pages" type="xs:string"/> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="id" type="xs:ID" use="required"/> <xs:attribute name="category" default="fiction"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="fiction"/> <xs:enumeration value="non-fiction"/> <xs:enumeration value="technical"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="language" type="xs:string" default="en"/> <xs:attribute name="edition" type="xs:string" default="1"/> <xs:attribute name="year" type="xs:string"/> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> 

11. DTD的实际应用

尽管XML Schema提供了更强大的功能,但DTD仍然在许多领域有广泛应用:

11.1 Web标准

许多Web标准使用DTD定义文档结构,如:

  • XHTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
  • SVG:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 

11.2 企业数据交换

许多企业使用DTD定义XML文档结构,用于系统间的数据交换。例如,电子数据交换(EDI)系统常使用DTD验证交易文档。

11.3 出版行业

出版行业广泛使用DTD定义文档结构,如DocBook是一种使用DTD的技术文档标准。

11.4 配置文件

许多应用程序使用XML作为配置文件格式,并使用DTD验证配置文件的结构。

12. 学习DTD的最佳实践

12.1 从简单开始

学习DTD时,从简单的文档结构开始,逐步增加复杂性。先掌握基本的元素和属性声明,再学习更高级的特性。

12.2 实践验证

创建DTD后,使用验证工具检查XML文档是否符合DTD定义。这有助于理解DTD如何约束文档结构。

12.3 参考标准

研究现有的DTD标准,如XHTML或DocBook,学习它们如何定义文档结构。

12.4 工具辅助

使用专门的XML编辑器(如XMLSpy、 Oxygen XML Editor)可以简化DTD的创建和验证过程。

12.5 持续学习

DTD只是XML验证的一种方式,继续学习XML Schema和其他相关技术,如XPath、XSLT等,可以更全面地掌握XML技术栈。

13. 总结

DTD是XML文档验证的重要工具,通过定义文档的结构、元素和属性,确保XML文档符合预定义的规范。尽管XML Schema提供了更强大的功能,但DTD因其简单性和广泛支持,仍在许多领域有重要应用。

本文详细介绍了DTD的基本概念、语法结构、元素和属性声明、实体定义以及高级特性,并通过实际示例展示了如何创建和使用DTD。通过学习本文,您应该能够理解DTD的核心概念,并能够创建自己的DTD来验证XML文档。

无论您是XML新手还是有经验的开发者,掌握DTD都将帮助您更好地理解和处理XML文档,为您的项目带来更高的数据质量和互操作性。