引言

XML(eXtensible Markup Language,可扩展标记语言)是一种用于存储和传输数据的标记语言,它被广泛应用于Web开发、数据交换和配置文件等领域。在XML中,DTD(Document Type Definition,文档类型定义)扮演着至关重要的角色,它定义了XML文档的结构、元素、属性以及它们之间的关系。

DTD可以看作是XML文档的”语法规则”,它确保XML文档遵循特定的结构和格式。通过使用DTD,我们可以验证XML文档的有效性,确保数据的一致性和完整性。本教程将带您深入了解DTD的概念、语法和应用,通过丰富的案例帮助您轻松掌握DTD的使用。

DTD基础

DTD是一套用于定义XML文档结构的规则集合。它规定了XML文档中可以包含哪些元素、元素的嵌套关系、元素可以拥有的属性以及属性的类型等。DTD本身不是XML文档,它有自己的语法规则。

一个基本的DTD声明通常包含以下部分:

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

其中:

  • <!DOCTYPE> 是DTD声明的关键字
  • 根元素 是XML文档的根元素名称
  • [ ] 内部包含具体的DTD声明内容

DTD可以嵌入在XML文档内部(内部DTD),也可以保存在单独的文件中(外部DTD)。外部DTD可以通过以下方式引用:

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

或者:

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

元素声明

在DTD中,元素声明是最基本的部分,它定义了XML文档中可以包含哪些元素以及元素的结构。元素声明的基本语法如下:

<!ELEMENT 元素名 内容模型> 

内容模型定义了元素可以包含的内容类型,主要包括以下几种:

1. 空元素

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

<!ELEMENT br EMPTY> 

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

<br/> 

2. 文本内容

元素只包含文本内容,不包含子元素,使用 (#PCDATA) 声明:

<!ELEMENT title (#PCDATA)> 

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

<title>XML DTD Tutorial</title> 

#PCDATA(Parsed Character Data)表示可解析的字符数据,即文本内容。

3. 子元素

元素包含其他子元素,通过列出子元素名称来声明:

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

这表示 book 元素必须包含 titleauthorpublisher 三个子元素,且顺序必须按照声明中的顺序。

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

<book> <title>XML DTD Tutorial</title> <author>John Doe</author> <publisher>Tech Books</publisher> </book> 

4. 混合内容

元素既可以包含文本内容,也可以包含子元素,使用混合内容模型声明:

<!ELEMENT paragraph (#PCDATA|em|strong)*> 

这表示 paragraph 元素可以包含文本内容,也可以包含 emstrong 子元素,或者它们的混合。

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

<paragraph>This is a <em>sample</em> paragraph with <strong>mixed</strong> content.</paragraph> 

5. 任何内容

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

<!ELEMENT misc ANY> 

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

<misc> <text>Some text</text> <number>42</number> More text </misc> 

元素出现的次数

在DTD中,我们可以使用特殊符号来控制元素出现的次数:

  • *:零次或多次
  • +:一次或多次
  • ?:零次或一次
  • 无符号:恰好一次

例如:

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

这表示:

  • title 元素必须出现且仅出现一次
  • author 元素必须出现至少一次
  • publisher 元素可以出现零次或一次
  • chapter 元素可以出现零次或多次

选择列表

使用 | 符号表示元素之间的选择关系:

<!ELEMENT payment (cash|credit|debit)> 

这表示 payment 元素必须包含 cashcreditdebit 中的一个子元素。

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

<payment> <credit>Visa</credit> </payment> 

组合使用

我们可以将上述符号组合使用,创建更复杂的内容模型:

<!ELEMENT order (customer, (item+|service+), payment, note?)> 

这表示 order 元素必须包含:

  • 一个 customer 元素
  • 一个或多个 item 元素,或者一个或多个 service 元素
  • 一个 payment 元素
  • 零个或一个 note 元素

属性声明

在DTD中,我们可以为元素声明属性,属性声明的基本语法如下:

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

属性类型

DTD支持多种属性类型,常见的有:

1. CDATA

CDATA(Character Data)表示属性值可以是任何字符数据:

<!ATTLIST book id CDATA #REQUIRED > 

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

<book id="bk001"> <!-- 内容 --> </book> 

2. 枚举类型

枚举类型限制属性值只能是预定义的几个值之一:

<!ATTLIST book status (available|checked-out|reserved) "available" > 

这表示 status 属性的值只能是 availablechecked-outreserved 中的一个,默认值是 available

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

<book status="checked-out"> <!-- 内容 --> </book> 

3. ID 和 IDREF

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

<!ATTLIST book book_id ID #REQUIRED > <!ATTLIST author book_ref IDREF #IMPLIED > 

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

<book book_id="bk001"> <!-- 内容 --> </book> <author book_ref="bk001"> <!-- 内容 --> </author> 

4. NMTOKEN 和 NMTOKENS

NMTOKEN(Name Token)表示属性值必须是有效的XML名称字符,NMTOKENS表示属性值可以是多个NMTOKEN,用空格分隔:

<!ATTLIST book isbn NMTOKEN #REQUIRED keywords NMTOKENS #IMPLIED > 

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

<book isbn="978-3-16-148410-0" keywords="XML programming tutorial"> <!-- 内容 --> </book> 

5. ENTITY 和 ENTITIES

ENTITY类型表示属性值是一个已声明的实体,ENTITIES类型表示属性值可以是多个实体,用空格分隔:

<!ENTITY logo SYSTEM "logo.png" NDATA PNG> <!ATTLIST book cover ENTITY #IMPLIED > 

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

<book cover="logo"> <!-- 内容 --> </book> 

默认值

DTD支持多种默认值设置方式:

1. #REQUIRED

属性必须提供值:

<!ATTLIST book id CDATA #REQUIRED > 

2. #IMPLIED

属性是可选的:

<!ATTLIST book edition CDATA #IMPLIED > 

3. #FIXED

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

<!ATTLIST book language CDATA #FIXED "en" > 

4. 默认值

为属性提供默认值:

<!ATTLIST book availability CDATA "in-stock" > 

实体声明

实体是DTD中的一个重要概念,它可以用来定义可重用的内容片段。实体声明的基本语法如下:

<!ENTITY 实体名 "实体内容"> 

内部通用实体

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

<!ENTITY company "Tech Books Inc."> 

在XML文档中,可以通过 &实体名; 引用实体:

<publisher>&company;</publisher> 

解析后,XML文档中的 &company; 将被替换为 Tech Books Inc.

外部通用实体

外部通用实体引用外部文件中的内容:

<!ENTITY footer SYSTEM "footer.xml"> 

在XML文档中,可以通过 &footer; 引用外部文件的内容。

参数实体

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

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

未解析实体

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

<!ENTITY logo SYSTEM "logo.png" NDATA PNG> <!NOTATION PNG SYSTEM "image/png"> 

在XML文档中,可以通过属性引用未解析实体:

<book cover="logo"> <!-- 内容 --> </book> 

DTD与XML文档的关联

DTD可以与XML文档以两种方式关联:内部DTD和外部DTD。

内部DTD

内部DTD直接嵌入在XML文档中,位于文档类型声明部分:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE book [ <!ELEMENT book (title, author, publisher)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT publisher (#PCDATA)> ]> <book> <title>XML DTD Tutorial</title> <author>John Doe</author> <publisher>Tech Books</publisher> </book> 

内部DTD的优点是简单、自包含,适合小型XML文档。缺点是如果多个XML文档使用相同的DTD,会导致代码重复。

外部DTD

外部DTD保存在单独的文件中,通常以 .dtd 为扩展名,然后在XML文档中引用:

book.dtd 文件内容:

<!ELEMENT book (title, author, publisher)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT publisher (#PCDATA)> 

XML 文档内容:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE book SYSTEM "book.dtd"> <book> <title>XML DTD Tutorial</title> <author>John Doe</author> <publisher>Tech Books</publisher> </book> 

外部DTD的优点是可以在多个XML文档之间共享,便于维护和更新。缺点是需要额外的文件,增加了复杂性。

公共DTD

公共DTD是一些标准化的DTD,可以通过公共标识符引用:

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

实际案例

让我们通过一个完整的案例来展示DTD的应用。假设我们要为一个图书管理系统创建XML文档结构,包括图书、作者和出版社信息。

步骤1:创建DTD文件

首先,我们创建一个名为 library.dtd 的DTD文件:

<!-- 图书馆DTD --> <!ENTITY % commonElements "title | author | publisher | isbn | publish-date | price"> <!-- 根元素 --> <!ELEMENT library (book+)> <!-- 图书元素 --> <!ELEMENT book (%commonElements;, description?, category+)> <!ATTLIST book book_id ID #REQUIRED status (available|checked-out|reserved|lost) "available" > <!-- 作者元素 --> <!ELEMENT author (name, nationality)> <!ATTLIST author author_id ID #REQUIRED > <!-- 出版社元素 --> <!ELEMENT publisher (name, address)> <!ATTLIST publisher publisher_id ID #REQUIRED > <!-- 基本元素 --> <!ELEMENT title (#PCDATA)> <!ELEMENT isbn (#PCDATA)> <!ELEMENT publish-date (#PCDATA)> <!ELEMENT price (#PCDATA)> <!ELEMENT description (#PCDATA)> <!ELEMENT category (#PCDATA)> <!ELEMENT name (#PCDATA)> <!ELEMENT nationality (#PCDATA)> <!ELEMENT address (#PCDATA)> <!-- 属性列表 --> <!ATTLIST price currency CDATA "USD" > <!ATTLIST category code IDREF #IMPLIED > 

步骤2:创建XML文档

接下来,我们创建一个符合上述DTD的XML文档:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE library SYSTEM "library.dtd"> <library> <book book_id="bk001" status="available"> <title>XML DTD Tutorial</title> <author> <name>John Doe</name> <nationality>American</nationality> </author> <publisher> <name>Tech Books Inc.</name> <address>123 Tech Street, Silicon Valley, CA</address> </publisher> <isbn>978-3-16-148410-0</isbn> <publish-date>2023-01-15</publish-date> <price currency="USD">29.99</price> <description>A comprehensive guide to learning XML and DTD.</description> <category code="cat001">Programming</category> <category code="cat002">Web Development</category> </book> <book book_id="bk002" status="checked-out"> <title>Advanced XML Techniques</title> <author> <name>Jane Smith</name> <nationality>British</nationality> </author> <publisher> <name>Tech Books Inc.</name> <address>123 Tech Street, Silicon Valley, CA</address> </publisher> <isbn>978-1-23-456789-7</isbn> <publish-date>2023-03-20</publish-date> <price currency="USD">39.99</price> <description>Exploring advanced concepts in XML and related technologies.</description> <category code="cat001">Programming</category> <category code="cat003">Data Management</category> </book> </library> 

步骤3:验证XML文档

我们可以使用XML解析器或验证工具来验证XML文档是否符合DTD规范。例如,使用Java的DOM解析器:

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

步骤4:使用DTD实体优化

我们可以使用实体来优化DTD,使其更加模块化和可重用:

<!-- 图书馆DTD --> <!ENTITY % commonElements "title | author | publisher | isbn | publish-date | price"> <!-- 基本元素声明 --> <!ENTITY % basicElements " <!ELEMENT title (#PCDATA)> <!ELEMENT isbn (#PCDATA)> <!ELEMENT publish-date (#PCDATA)> <!ELEMENT price (#PCDATA)> <!ELEMENT description (#PCDATA)> <!ELEMENT category (#PCDATA)> <!ELEMENT name (#PCDATA)> <!ELEMENT nationality (#PCDATA)> <!ELEMENT address (#PCDATA)> "> <!-- 属性声明 --> <!ENTITY % commonAttributes " <!ATTLIST price currency CDATA 'USD' > <!ATTLIST category code IDREF #IMPLIED > "> <!-- 根元素 --> <!ELEMENT library (book+)> <!-- 图书元素 --> <!ELEMENT book (%commonElements;, description?, category+)> <!ATTLIST book book_id ID #REQUIRED status (available|checked-out|reserved|lost) "available" > <!-- 作者元素 --> <!ELEMENT author (name, nationality)> <!ATTLIST author author_id ID #REQUIRED > <!-- 出版社元素 --> <!ELEMENT publisher (name, address)> <!ATTLIST publisher publisher_id ID #REQUIRED > <!-- 包含基本元素和属性声明 --> %basicElements; %commonAttributes; 

通过使用参数实体,我们可以将DTD分解为多个模块,提高可维护性和重用性。

DTD的优缺点

优点

  1. 简单易学:DTD的语法相对简单,容易学习和使用。
  2. 广泛支持:DTD是XML规范的一部分,几乎所有的XML解析器都支持DTD验证。
  3. 定义文档结构:DTD可以清晰地定义XML文档的结构,确保数据的一致性。
  4. 实体支持:DTD支持实体,可以用于创建可重用的内容片段。
  5. 内置数据类型:虽然有限,但DTD提供了一些基本的数据类型,如ID、IDREF等。

缺点

  1. 数据类型有限:DTD只提供了有限的数据类型,不支持更复杂的数据类型定义。
  2. 非XML语法:DTD使用自己的语法,不是XML格式,这增加了学习和使用的复杂性。
  3. 命名空间支持有限:DTD对XML命名空间的支持有限,这在处理大型XML文档时可能成为问题。
  4. 缺乏扩展性:DTD的扩展性较差,难以适应复杂的应用场景。
  5. 文档验证能力有限:DTD只能验证文档的结构,不能验证内容的语义。

DTD与XML Schema的比较

随着XML技术的发展,XML Schema(XSD)作为DTD的替代方案出现,提供了更强大的功能。以下是DTD与XML Schema的主要区别:

语法格式

  • DTD:使用非XML语法
  • XML Schema:使用XML语法,更易于理解和处理

数据类型

  • DTD:提供有限的数据类型(CDATA、ID、IDREF等)
  • XML Schema:提供丰富的内置数据类型,并支持自定义数据类型

命名空间支持

  • DTD:对命名空间的支持有限
  • XML Schema:完全支持命名空间,适合处理复杂的XML文档

扩展性

  • DTD:扩展性较差
  • XML Schema:支持继承、扩展和限制,具有更好的扩展性

文档验证

  • DTD:只能验证文档结构
  • XML Schema:可以验证文档结构和内容语义

示例比较

让我们比较一下定义相同结构的DTD和XML Schema:

DTD示例:

<!ELEMENT book (title, author, publisher)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT publisher (#PCDATA)> <!ATTLIST book isbn CDATA #REQUIRED year CDATA #IMPLIED > 

等效的XML Schema示例:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="book"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> <xs:element name="publisher" type="xs:string"/> </xs:sequence> <xs:attribute name="isbn" type="xs:string" use="required"/> <xs:attribute name="year" type="xs:gYear" use="optional"/> </xs:complexType> </xs:element> </xs:schema> 

从上面的例子可以看出,XML Schema提供了更丰富的数据类型(如xs:gYear)和更灵活的结构定义方式。

总结

DTD(Document Type Definition)是XML文档类型定义的重要工具,它定义了XML文档的结构、元素、属性以及它们之间的关系。通过本教程,我们学习了DTD的基本概念、语法规则和使用方法,包括元素声明、属性声明、实体声明等内容。

DTD的主要优点是简单易学、广泛支持,适合定义简单的XML文档结构。然而,它也存在数据类型有限、非XML语法、命名空间支持有限等缺点。对于更复杂的XML应用场景,可以考虑使用XML Schema(XSD)作为替代方案。

在实际应用中,DTD仍然是一种有用的工具,特别是在处理简单的XML文档或需要向后兼容的场景。通过掌握DTD的基本知识和技能,您可以更好地理解和使用XML技术,为数据交换和文档处理提供坚实的基础。

希望本教程能够帮助您轻松掌握DTD,并在实际工作中灵活应用。如果您想进一步学习XML相关技术,建议继续探索XML Schema、XSLT、XPath等更高级的主题。