Web Services应用开发
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第2章 XML文档类型定义

本章目标

■ 了解DTD的作用

■ 掌握内部DTD的使用

■ 掌握外部DTD的使用

■ 掌握DTD的文档结构

■ 掌握DTD中不同类型元素的定义及使用

■ 掌握DTD中各种属性的定义及使用

■ 掌握DTD中实体的定义及使用

学习导航

任务描述

【描述2.D.1】

创建并使用内部DTD规范XML文档。

【描述2.D.2】

创建并使用外部DTD规范XML文档。

【描述2.D.3】

创建并使用带属性的DTD,演示CDATA的使用。

【描述2.D.4】

创建并使用带NMTOKEN属性的DTD,演示NMTOKEN的使用。

【描述2.D.5】

创建并使用带ID属性的DTD,演示ID的使用。

【描述2.D.6】

创建并使用带IDREF(IDREFS)属性的DTD,演示IDREF的使用。

【描述2.D.7】

创建并使用带枚举类型属性的内部DTD,演示枚举类型的使用。

【描述2.D.8】

创建并使用带自定义实体的内部DTD,演示自定义实体的使用。

2.1 DTD概述

XML的精髓在于“无障碍”数据交换和数据共享,编写格式良好的XML文档,使其他用户能够理解符合我们创建的XML词汇表(包括元素及属性等)的文档结构,须通过某种通用的方式说明词汇表的语法规则。为此,XML1.0提供了一种机制——文档类型定义(Document Type Definition,DTD),并将其作为规范的一部分。DTD使用正式的语法定义XML文档的结构和允许值。

2.1.1 DTD简介

XML的最大灵活性在于允许用户制定基于信息描述、体现数据之间逻辑关系的自定义标记,确保文档具有较强的易读性、清晰的语义和易检索性。因此,一个完全意义上的XML文档不仅仅是“Well Formed”(格式良好的),而且还应该是使用了一些自定义标记的“Validating XML”(有效的)文档,即它必须遵守文档类型定义DTD中已声明的各种规定。文档类型定义DTD,用来描述XML文档的结构。DTD定义了XML文档中可用的合法元素,并且定义了可以在文档中存在的元素、元素可以具有的属性、元素的层次结构,以及元素在整个文档中出现的顺序。简而言之,DTD规定了一个语法分析器以解释一个“Validating XML”文档所需要知道的所有规则的细节。

DTD可以看做是一个或多个XML文件的模板,这些XML文件中的元素、元素的属性、元素的排列方式/顺序、元素能够包含的内容等,都必须符合DTD中的定义。XML文件中的元素(标记)是根据应用的实际情况来创建的。想要创建一份完整性高、适应性广的DTD是非常困难的,因为各行各业都有他们自己的行业特点,所以DTD通常是以某种应用领域为定义的范围,如:生产、医学、工商、金融。DTD定义的元素涵盖范围越广泛,它就越复杂。

引入DTD的优势如下所示。

引入DTD,每个XML文件可以携带一个自身格式的描述。

■ 引入DTD,不同组织的人可以使用一个通用DTD交换数据,应用程序可以使用一个标准DTD校验从外部世界接收来的XML数据是否有效。

■ 引入DTD,便于在网络上进行数据的共享和交互。例如,两个相同行业不同地区的人使用同一个DTD文件作为文档的创建规范,那么他们的数据就很容易交换和共享。网络上其他用户补充的数据,只需要根据公用的DTD规范来建立即可。

目前,针对不同的行业和应用,已经有数量众多的写好的DTD文件可以利用,这些DTD文件已经建立了通用的元素和标签规则。用户无须重新创建,只要在这些DTD文件的基础上加入特定的新元素即可。当然,用户也可以创建自己的DTD。

2.1.2 DTD声明

DTD是与XML文档相关的。通常,XML文档中通过DOCTYPE声明将XML与DTD建立关联的指令,当验证有效性的解析器读到该指令时,它获取相应的DTD,并根据其中定义的规则对文档进行校验。

DOCTYPE声明由以下部分组成:关键字、文档的根元素名称、可选的外部标记符,以及可选的标记声明块。外部标记符用于外部DTD(外部子集)的命名和定位,标记声明块是由标记声明(内部子集)构成的。

DOCTYPE声明语法示例如下:

<?xml version="1.0" standalone="no" encode="UTF-8"?>
<!DOCTYPE  Catalog ...>
<Catalog >

第2行中使用“<!DOCTYPE>”标记,该标记就是DOCTYPE声明,说明该文档的第一个元素(根元素)是Catalog,省略号“…”隐藏了DOCTYPE声明的其余部分。

XML规范定义了两种DOCTYPE声明的方法。

内部DTD声明:在DOCTYPE声明体中直接定义内部标记子集。

■ 外部DTD声明:用户可以在独立的DTD文件中提供外部标记子集声明,再通过DOCTYPE声明将其引入。

这两种声明方式可以同时使用。

注意 DOCTYPE声明必须位于XML声明之后,且在任何文档元素之前。但是,XML声明和DOCTYPE声明之间可以插入注释和处理指令。

2.1.3 内部DTD

可以使用“<!DOCTYPE[.....]>”语句在XML文档序言部分声明DTD文档的定义,引入内部DTD的XML文档的语法结构如下:

<?xml version = "1.0" encoding="GB2312"?>
<!DOCTYPE element-name[
元素描述
]>
<!--文档数据区.......-->

其中:

<!DOCTYPE:表示开始设定DTD,注意DOCTYPE必须大写。

■ element-name:指定此DTD的根元素的名称,一个XML文件只能有一个根元素。注意,如果XML文档使用了DTD,那么文件中的根元素就在这里指定。

■ [ ]>:在[ ]标记里面定义XML文件中使用的元素,然后用“>”结束DTD的定义。

下述代码用于实现任务【描述2.D.1】,创建并使用内部DTD规范XML文档。

【描述2.D.1】 student_1.xml
1.<?xml version="1.0" encoding="GB2312"?>
2.<!DOCTYPE students [
3.<!ELEMENT students (student)+>
4.<!ELEMENT student (name,age,tel)>
5.<!ELEMENT name (#PCDATA)>
6.<!ELEMENT age (#PCDATA)>
7.<!ELEMENT tel (#PCDATA)>
8.]>
9.<students>
10.   <student>
11.       <name>Tom</name>
12.       <age>14</age>
13.       <tel>88889999</tel>
14.   </student>
15.</students>

上述文档是一个格式良好,并且有效(满足DTD校验)的XML文档,其中:

* 第1行代码是一个XML声明,表示文档遵循的是XML的1.0版的规范。

* 第2~8行即为内部DTD的定义内容。这个内部DTD中,定义了名为students的元素是XML文档中的根元素,此根元素拥有至少一个student子元素,子元素student有name、age和tel三个子元素。name、age和tel元素都为“#PCDATA”类型。

注意 在进行DTD书写时,特别注意元素定义时,需要在元素名称和内容之间加空格隔开,如:“<!ELEMENT students”和“(student)+>”之间要通过空格隔开。

2.1.4 外部DTD

外部DTD是一个独立于XML文档的文件,实际上也是一个文本文件,只是使用.dtd作为文件扩展名。外部DTD独立于XML文档,所以它可以供多个XML文件使用,就像用同一个模板可以写出多个不同内容的文件一样。

使用外部DTD的好处是:它可以方便高效地被多个XML文档所共享。只要写一个DTD文档,就可以被多个XML文档所引用。

除了没有内部DTD中的<!DOCTYPE [.....]>语句外,外部DTD的创建方式、语法和内部DTD是一样的。XML文档使用DTD文件的声明语句来引用创建好的外部DTD文件,此语句必须位于XML文档的文件序言区,并且要紧跟在XML声明语句后面,DTD文件的声明语句格式如下:

<!DOCTYPE type-of-doc SYSTEM/PUBLIC "dtd-name">

具体介绍如下所示。

* <!DOCTYPE:是指要定义一个DOCTYPE。

* type-of-doc:指定文档类型的名称,由用户自己定义,通常与使用这个DTD文件的XML文档的根元素名称一致。

* SYSTEM/PUBLIC:这两个参数只用其一。SYSTEM是指文档使用的是私有的外部DTD文件,这个关键字主要用于引用一个作者或组织所编写的众多XML文档中通用的DTD;而PUBLIC则指文档调用一个公用的外部DTD文件,此外部DTD文件经过了公开讨论,即一个由权威机构制定的、提供给特定行业或公众使用的DTD。

* dtd-name:是存放DTD文件的地址和名称。

综上所述,可以这样理解引入外部DTD的XML文档的结构,即:

< ?xml version = "1.0"?>
<!DOCTYPE 根元素名 SYSTEM/PUBLIC "外部DTD文件名及其位置">
<!--文档数据区.......-->

下述代码用于实现任务【描述2.D.2】,创建并使用外部DTD规范XML文档。

定义外部DTD文档内容如下所示。

【描述2.D.2】 student_2.dtd
<?xml version="1.0" encoding="GB2312"?>
<!ELEMENT students (student+)>
<!ELEMENT student (name,age,tel*)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT tel (#PCDATA)>

关联外部DTD文件的XML的总体代码如下:

【描述2.D.2】 student_2.xml
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE students SYSTEM "student_2.dtd">
<students>
    <student>
        <name>Tom</name>
        <age>14</age>
        <tel>88889999</tel>
    </student>
</students>

上述代码中,第2行代码的作用就是将外部DTD文件student_2.dtd应用于此XML文档。

2.2 DTD语法

DTD是保证XML文档有效性的方法之一,可使XML解析器通过比较XML文档和DTD文件来确定文档是否符合规范、元素和标签使用是否正确等。一个DTD文档包含:元素的定义规则、元素间关系的定义规则、元素可使用的属性以及可使用的实体或符号规则等。DTD文件也是一个ASCII的文本文件,后缀名为.dtd。

2.2.1 元素声明

元素是XML的核心与灵魂,它包含了实际的文档信息,并指出了这些信息的逻辑结构。元素以树形分层机构排列,可以嵌套在其他元素中。

在DTD中,元素类型是通过ELEMENT标记声明的。除了关键字,标记还提供所声明类型的名称和内容规范。正如第1章所述,元素类型名要遵守XML对名称的限制。名称可以是字母、数字,也可以使用标点符号,如:冒号(:)、下画线(_)、连字符(-)和句点(.)。然而,名称不能以数字开头。它的第一个字符只能是字母、下画线或冒号。

注意 虽然名称中可以使用冒号,但是鉴于后面即将介绍的命名空间,最好避免在元素名称中使用冒号。

DTD中使用元素类型声明ETD(Element Type Declaration)来声明所有有效的文档元素。其语法结构如下:

<!ELEMENT element-name element-definition>

具体介绍如下所示。

* <!ELEMENT:表示开始元素设置,注意此处ELEMENT关键字必须是大写。

* element-name:表示要设置的元素的名称。

* element-definition:指明要对此元素进行怎样的定义,就是说<元素>...</元素>之间能够包含什么内容,是其他元素还是一般性的文字。

XML中按元素的内容(element-definition)可以将元素划分为五类,不同类型的元素使用DTD进行定义时的语法如下。

1.ANY类型

如果不需要对元素的内容进行限制,可以在DTD中使用ANY元素类型。定义方式为:

<!ELEMENT element-name ANY>

XML文档里该元素中可以包含任何在DTD中定义的元素内容。建议一般只把文档的根元素规定为ANY类型。将根元素设为ANY类型后,元素出现的次数和顺序不受限制。

注意 为了增强可读性,每个元素定义通常占据单独的一行。

2.EMPTY类型

EMPTY元素类型。定义方式为:

<!ELEMENT element-name EMPTY>

这类元素在XML文档中使用空元素标记,即元素中没有内容,不能包含子元素和文本,但可以有属性。例如,对于下面的DTD定义:

<!ELEMENT student EMPTY>

以下这个元素是合法的:

<student name="Jack" age="16"/>

而下面这些元素的定义就是非法的,因为此元素应该为空元素,既不能有内容也不能有子元素:

<student>Jack</student>
<student><name>Jack</name></student>
3.#PCDATA类型

#PCDATA类型的元素(纯文本元素,或称简单元素)可以包含任何字符数据,但是不能在其中包含任何子元素。PCDATA代表字符数据,为防止与关键字混淆,需加“#”前缀。其定义方式为:

<!ELEMENT element-name (#PCDATA)>

下面的DTD定义说明name只能包含字符数据,即非标记文本,并且它不能包含自己的子元素:

<!ELEMENT name (#PCDATA)>

下面这些元素定义都是合法的:

<name>Jack</name>
<name>2008</name>
<name>123.456</name>

因为XML不会去检查PCDATA的内容,只要是文本就可以。但下面的元素定义是非法的:

<name>
    <firstname>White</firstname>
    <lastname>Smith</lastname>
</name>
4.父元素类型

父元素类型只能包含子元素,并且这些子元素外没有文本。这类元素在DTD中通过正则表达式规定子元素出现的顺序和次数。语法分析器将这些正则表达式与XML文档内部的数据模式相匹配,从而判断该文档是否是有效的XML。表2-1列出了在DTD中用到的正则表达式的元组运算符。

表2-1 正则表达式的元组运算符

表2-2通过示例介绍了上述元组运算符的用法。

表2-2 用法示例

对于下述DTD定义:

<!ELEMENT students (student+)>
<!ELEMENT student (name,age,tel*)>

表明:

students元素是父元素,它可以有至少一个student子元素(由“+”表示);student元素必须有一个name元素、一个age元素、零个或多个tel元素(由“*”表示),而且name、age、tel出现的次序必须一致。

5.混合元素类型

混合元素类型是包含子元素和文本数据的混合体,混合元素的定义方式为:

<!ELEMENT 元素名 (#PCDATA | element-name1 | element-name2 | ……)*>

对于下述DTD定义就是一个混合元素:

<!ELEMENT students (#PCDATA|student)*>

在DTD定义中,带有字符数据的混合元素只能指定可出现的子元素的名称,而不能限定它们出现的顺序及每个元素的出现次数,或者它们是否出现,严重地限制文档的结构。因此,最好避免使用混合元素。

注意 混合元素在声明时,其语法结构中除了可以改变子元素数目以外,其他的任何改动都是不合法的。也就是说,不能在包括“#PCDATA”的元素声明中使用逗号、问号或加号;用竖线分隔的元素和“#PCDATA”的列表是合法的。其他用法是不合法的。

2.2.2 属性声明

属性是对元素的补充和修饰,它能够将一些简单的特性与元素相关联。通过属性,可以给元素绑定大量信息。例如,在HTML标记IMG中,SRC就是一个属性。属性在DTD中是使用ATTLIST标记声明的。对于含属性的元素,至少要通过一个ATTLIST标记声明其属性列表。ATTLIST声明由以下部分构成:ATTLIST关键字、属性修饰的元素名称,以及零个或多个属性定义。属性的定义方式为:

<!ATTLIST 元素名 属性名称 属性类型 取值方式 >

实际运用中,一个元素往往不只有一个属性,当需要为一个元素定义多个属性时,也可采用如下定义方式:

<!ATTLIST 元素名
    属性名称 属性类型 取值方式
    属性名称 属性类型 取值方式
    ...
>

其中,属性取值方式如表2-3所示。

表2-3 属性取值方式

属性类型用来指定该属性的取值类型,DTD的属性类型如表2-4所示。

表2-4 属性类型

下面通过实例,对常用的属性类型及属性取值方式进行详细介绍。

1.CDATA

声明为CDATA的属性值可以是任何文本串(包括数字和中文),但不能包含“<”,“&”,“"”等字符串,对于下述属性声明:

<!ATTLIST student name CDATA #REQUIRED>

在XML文档中,下列写法都是合法有效的:

<student name="Jack"/>
<student name="杰克"/>
<student name="123456"/>

下述代码用于实现任务【描述2.D.3】,创建并使用带属性的内部DTD,演示CDATA的使用。

【描述2.D.3】 family.xml
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE family [
    <!ELEMENT family (person+, appliance*)>
    <!ELEMENT person EMPTY>
    <!ELEMENT appliance EMPTY>
    <!ATTLIST person
        name CDATA #REQUIRED
        age CDATA #REQUIRED
        tel CDATA #IMPLIED
>
    <!ATTLIST appliance
        name CDATA #REQUIRED
        quantity CDATA "1"
        comments CDATA #IMPLIED
>
]>
<family>
    <person name="Rose" age="25"/>
    <person name="杰克" age="28" tel="88887777"/>
    <appliance name="冰箱" quantity="2"/>
</family>

上述代码中为person元素声明了三个属性:name、age和tel,其中tel可有可无,其他两个属性必须出现;为appliance声明了三个属性,comments可有可无,name和quantity是必须出现的,并且属性quantity声明默认值:1。上述XML文档合法有效。

如果将XML文档的XML数据部分写为如下形式:

<family>
    <person name="Rose" /> <!-- Error,缺少age属性-->
    <person name="杰克" age="28" tel="88887777"/>
    <appliance name="冰箱" color="red"/><!-- Error,未定义color属性-->
</family>

将提示XML验证错误信息,原因是此XML数据部分声明未遵循其关联的DTD文档规范。

2.NMTOKEN/NMTOKENS

某些情况下,可能希望将属性值作为标记,代表某种特殊的意义,如:通过标记信息告知解析器对于此文件的处理级别、安全级别等。此时就需要XML中的NMTOKEN(name token,名称记号),NMTOKEN是CDATA的一个子集,表示属性值必须是英文字母、数字、句点、连字符、下画线或冒号组成,即满足XML名称规范。

注意 NMTOKEN与元素和属性名称并不完全相同,NMTOKEN的第一个字符可以是任意字符,但建议与XML名称规范一致,方便阅读。

下述代码用于实现任务【描述2.D.4】,创建并使用带NMTOKEN属性的内部DTD,演示NMTOKEN的使用。

【描述2.D.4】 NMTOKENdemo.xml
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE poems [
    <!ELEMENT poems (poem*)>
    <!ELEMENT poem (title, content)>
    <!ATTLIST poem security_level NMTOKEN #REQUIRED>
    <!ELEMENT title (#PCDATA)>
    <!ELEMENT content (#PCDATA)>
]>
<poems>
    <poem security_level="all">
        <title >黄鹤楼</title>
        <content>
            昔人已乘黄鹤去,此地空余黄鹤楼。黄鹤一去不复返,白云千载空悠悠。
            晴川历历汉阳树,芳草萋萋鹦鹉洲。日暮乡关何处是?烟江波上使人愁。
        </content>
    </poem>
</poems>

上述代码表示元素poem有一个名为security_level的属性,其值符合XML名称规则。用户可以通过该属性来控制文档的访问。由于定义属性列表时使用了NMTOKEN,用户只需创建新值就能适应新的安全级别要求,而不必每次都编辑DTD。

NMTOKENS与NMTOKEN类似,是NMTOKEN的复数,每个NMTOKEN之间通过空格隔开。下述代码演示了NMTOKENS的使用:

<!ATTLIST poem security_level NMTOKENS #REQUIRED>
<poem security_level="role1 role2 role5">
……
</poem>

在这里,该poem实例可以被role1、role2和role5访问。就类型而言,这些数据都是有效的NMTOKEN值。

注意 NMTOKENS的值与枚举类型不同,解析器不检查这些NMTOKENS取值的有效性,用户必须确保自己使用了适当的名称。

3.ID

ID类型的属性表明该属性的取值在整个文档中必须是唯一的。它可以作为元素的唯一标识符。每个元素至多有一个ID类型的属性。该ID必须以一个字母开头。

下述代码用于实现任务【描述2.D.5】,创建并使用带ID属性的内部DTD,演示ID的使用。

【描述2.D.5】 IDdemo.xml
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE poems [
    <!ELEMENT poems (poem*)>
    <!ELEMENT poem (content)>
    <!ATTLIST poem
        NO ID #REQUIRED
        title CDATA #REQUIRED>
    <!ELEMENT content (#PCDATA)>
]>
<poems>
    <poem NO="Z001" title="黄鹤楼">
        <content>
            昔人已乘黄鹤去,此地空余黄鹤楼。黄鹤一去不复返,白云千载空悠悠。
            晴川历历汉阳树,芳草萋萋鹦鹉洲。日暮乡关何处是?烟江波上使人愁。
        </content>
    </poem>
    <poem NO="Z002" title="望庐山瀑布">
        <content>
            日照香炉生紫烟,遥看瀑布挂前川。飞流直下三千尺,疑是银河落九天。
        </content>
    </poem>
</poems>

上述文档的内部DTD将属性NO声明为ID类型的,XML的数据部分是合法有效的。如果将两个元素poem属性NO的取值都改为“Z001”和“Z002”或分别改为“001”和“002”,这两种情况都将提示相关的错误信息。

注意 ID类型的属性必须设置为#IMPLIED或#REQUIRED,不能是#FIXED或默认的,不能为ID提供默认值。

4.IDREF/IDREFS

IDREF属性的值指向文档中其他地方声明的ID类型的值。在应用程序中,通过ID和IDREF实现交叉引用,而不必多次重复整个元素。

IDREF属性的值与ID类型的取值约束一致,而且它必须与文档中的某个ID属性具有相同的值。IDREF值不能指向文档中不存在的ID。IDREFS与IDREF类似,是IDREF的复数,每个IDREF之间通过空格隔开。

下述代码用于实现任务【描述2.D.6】,创建并使用带IDREF(IDREFS)属性的内部DTD,演示IDREF的使用。

【描述2.D.6】 IDREFdemo.xml
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE family [
    <!ELEMENT family (person+)>
    <!ELEMENT person EMPTY>
    <!ATTLIST person
        relID ID #REQUIRED
        parentID IDREFS #IMPLIED
        name CDATA #REQUIRED
>
]>
<family>
    <person relID="P01" name="Bernie"/>
    <person relID="P02" name="Rose"/>
    <person relID="P03" parentID="P01" name="Joson"/>
    <person relID="P04" name="Tom"/>
    <person relID="P05" name="Barbara"/>
    <person relID="P06" parentID="P04 P05" name="Barbie"/>
</family>

上述文档中,通过交叉引用,应用程序(或XML解析器)可以通过元素的parentID属性找到其引用的person元素。

5.枚举类型

枚举类型(Enumerated)不需要使用关键字,它只是将所有可能的属性取值列举出来,并以竖线(“|”)分隔。枚举类型的每一个可能值都必须遵循XML的名称规范。

下述代码用于实现任务【描述2.D.7】,创建并使用带枚举类型属性的内部DTD,演示枚举类型的使用。

【描述2.D.7】 ENUMdemo.xml
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE students [
    <!ELEMENT students (student+)>
    <!ELEMENT student EMPTY>
    <!ATTLIST student
        name CDATA #REQUIRED
        age CDATA #REQUIRED
        sex (male | female) "male"
>
]>
<students>
    <student name="Tom" age="17"/>
    <student name="Rose" age="19" sex="female"/>
</students>

上述文档中,元素student的sex属性被声明为枚举类型,可能的取值为male或female,并被设置默认取值为male。在实际应用中,元素student的sex属性的取值必须为上述二者取值之一。

2.2.3 实体

实体(Entity)是XML中一种可以节省大量时间的机制,它的作用类似于Word中的“宏”,也可以将其理解为模板,使用者可以预先定义一个实体,然后在一个文档中多次调用,或者在多个文档中调用同一个实体。简单来说,实体就是XML引用一个数据项的方法,它通常是文本,也可以是二进制数据。

一般情况下,实体的命名遵循XML名称规范即可,使用实体的好处在于以下两个方面。

* 减少出错率,文档中多个相同的部分只需要输入一遍即可。

* 提高维护效率,比如多个文档都包含地址信息的实体,如果需要修改这个地址信息,只要修改最初定义的实体语句即可。

XML定义了两种类型的实体:一种是预定义实体;另一种是自定义实体。

1.预定义实体

在XML中,对于XML语言定义的某些特殊字符(如尖括号)需保留定义,另外,有些字符是不可打印的。XML提供了一些预定义的实体,如表2-5所示,使用者可以在XML文档中利用这些实体表示特定的字符,并保证不产生冲突。因此在元素的文本内容中,可以用实体表示一些特殊字符,以免它们在解析时与文档的标记混淆。

表2-5 XML的预定义实体

在XML中,任何字符也都可以表示为数字引用。具体方法是在符号“&#”之后加上字符的数字值和分号(它们之间没有空格)。例如,大于号也可以表示为“&#62;”。

2.自定义实体

使用者也可以根据需要在DTD中定义一个实体,然后在文档中通过引用来使用它。

自定义实体的语法格式为:

<!DOCTYPE rootname [
<!ENTITY entity-name "entity-content">
]>

下述代码定义了一段地址信息:

<!DOCTYPE ADDRESS [
<!ENTITY ADDRESS "山东路家乐福">
]>

如果此地址信息的内容和他人所需的信息来源于同一个文件,也可以使用外部调用的方法。引用格式如下:

<!DOCTYPE ADDRESS [
<!ENTITY ADDRESS SYSTEM "http://www.sample.com/ADDRESS.xml">
]>

定义好的实体在文档中的引用语法为“&实体名;”。

下述代码用于实现任务【描述2.D.8】,创建并使用带自定义实体的内部DTD,演示自定义实体的使用。

【描述2.D.8】 ENTITYdemo.xml
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE greeses [
    <!ELEMENT greeses (greese+)>
    <!ELEMENT greese (name, letter, description?)>
    <!ELEMENT name (#PCDATA)>
    <!ELEMENT letter (#PCDATA)>
    <!ELEMENT description (#PCDATA)>
    <!ENTITY alpha "α">
    <!ENTITY beta "β">
]>
<greeses>
    <greese>
        <name>alpha</name>
        <letter>&alpha;</letter>
        <description>第一个希腊字母</description>
    </greese>
    <greese>
        <name>beta</name>
        <letter>&beta;</letter>
        <description>第二个希腊字母</description>
    </greese>
    <greese>
        <name>小于号</name>
        <letter><</letter>
    </greese>
</greeses>

通过IE浏览器查看结果如图2-1所示。

图2-1 实体显示结果

需要注意的是,实体允许嵌套,如下述代码是允许的:

<!ENTITY man "Mr.">
<!ENTITY name "Johnson">
<!ENTITY welcome "&man; &name;">

但是,实体不允许循环,如下述代码是错误的:

<!ENTITY man "Mr.&name;">
<!ENTITY name "&man; Johnson">

当XML解析器处理这种情况时,会陷入死循环,用户在定义实体时,务必要严防这种事情的发生。

小结

通过本章的学习,学生应该能够学会:

* DTD是一套关于标记符的语法规则,是XML 1.0规格的一部分。

* DTD是一种保证XML文档格式正确的有效方法。

* DTD文件是一个ASCII的文本文件,后缀名为.dtd。

* DTD可以在XML内部定义也可以在外部定义。

* DTD提供了XML文档所包含的元素、属性和实体及相互关系的定义。

* DTD的元素类型有ANY类型、EMPTY类型、#PCDATA类型、父元素类型和混合元素类型。

* 常用的DTD属性类型有CDATA、NMTOKEN/NMTOKENS、ID、IDREF/IDREFS、枚举类型等。

* XML中实体可以理解为模板,使用者可以预先定义一个实体,然后在一个文档中多次调用,或者在多个文档中调用同一个实体。

* 实体是XML引用一个数据项的方法,它通常是文本,也可以是二进制数据。

* XML定义了两种类型的实体。一种是预定义实体;另一种是自定义实体。

练习

1.下列选项中,______是合法的元素名。

A.TOM B.1abc C.18 D.[abc]

2.对于下面的代码,______不是PRODUCT元素的子元素。

<!ELEMENT PRODUCT (PRODUCTNAME, DESCRIPTION, PRICE, QUANTITY)>

A.PRODUCTNAME B.QUANTITY

C.DESCRIPTION D.NUMBER

3.DTD文档中,定义属性的关键字是______。

A.DOCTYPE B.ATTLIST

C.ELEMENT D.ENTITY

4.下列选项中,______是预定义实体(多选)。

A.& B.&name; C.< D.&copyright;

5.DTD文档中,某元素属性的特点为必须包含该属性,该属性应定义为______。

A.#REQUIRED B.#IMPLIED

C.#FIXED value D.默认值

6.在DTD中,元素类型通过_________标记声明,实体类型通过_________标记声明。

7.DOCTYPE声明由以下部分组成:_________、_________、可选的外部标记符,以及_________。

8.属性类型设为ID,表明该属性的取值_________。

9.XML定义了两种类型的实体,一种是_________;另一种是_________。

10.IDREF属性的值指向文档中其他地方声明的_________类型的值。

11.分析下列XML实例,上机编写外部DTD文档,并进行验证。

<?xml version="1.0" encoding="GB2312"?>
<班级>
    <学生 学号="C95001">
        <姓名>李明</姓名>
        <性别>男</性别>
        <年龄>18</年龄>
    </学生>
    <学生 学号="C95002">
        <姓名>张燕</姓名>
        <性别>女</性别>
        <年龄>19</年龄>
    </学生>
    <学生 学号="C95003">
        <姓名>赵彦</姓名>
        <性别>男</性别>
        <年龄>20</年龄>
    </学生>
</班级>

12.分析下列DTD文档,上机编写有效的XML实例,并进行验证。

<!DOCTYPE NEWSPAPER [
<!ELEMENT NEWSPAPER (ARTICLE+)>
<!ELEMENT ARTICLE (HEADLINE,BYLINE,LEAD,BODY,NOTES)>
<!ELEMENT HEADLINE (#PCDATA)>
<!ELEMENT BYLINE (#PCDATA)>
<!ELEMENT LEAD (#PCDATA)>
<!ELEMENT BODY (#PCDATA)>
<!ELEMENT NOTES (#PCDATA)>
<!ATTLIST ARTICLE AUTHOR CDATA #REQUIRED>
<!ATTLIST ARTICLE EDITOR CDATA #IMPLIED>
<!ATTLIST ARTICLE DATE CDATA #IMPLIED>
<!ATTLIST ARTICLE EDITION CDATA #IMPLIED>
<!ENTITY NEWSPAPER "人民日报">
<!ENTITY PUBLISHER "人民日报出版社">
<!ENTITY COPYRIGHT "人民日报出版社版权所有>
]>