DTD简介
DTD是一套关于标记的语法规则,它定义了文档的逻辑结构,规定了文档中所使用的元素、实体、元素的属性、元素与实体之间的关系。DTD告诉你可以在哪些文档中使用哪些标记,各标记出现的次序,哪些标记出现在哪些标记中,哪些标记有属性等等。
DTD是XML文件的验证机制,定义好DTD,就可以使用XML解析器对编写好的XML文档进行DTD检查,判断XML文档内容是否为有效的XML文档内容。
为什么使用DTD?
1. 验证接收到的数据是否有效
2. 用于验证自己的数据
3. 定义 XML 文档的合法构建块
4. 使用一系列合法元素来定义文档结构
使用DTD的好处
用DTD提供统一格式。例如,用DTD规定个人简历文档的逻辑结构,包括所需填写内容和内容排列方式,所有按照这个DTD编写的个人简历都将具有统一格式。
用DTD验证数据有效性。DTD约束文档逻辑结构,可以根据DTD检查数据,验证其是否符合规定要求,确保数据正确和有效。
根据DTD编写文档样式单。DTD能在不提供原始资料的情况下,表示出一个网页或文档的架构元素,这意味着用户能先根据DTD为未来的文档编写样式单,然后再安全且不破坏结构的把用户的资料放上去,提高工作效率。
引入DTD
使用DTD定义了合法的语义约束后,必须让XML文档引入该语义约束,以表明该XML文档遵守哪种语义约束。在XML文档中引入DTD主要有3种方式:
内部DTD
外部DTD
公用DTD
内部DTD
所谓内部DTD是指DTD与XML数据定义放在同一份文档中,即将DTD定义在XML文档内部。内部DTD紧跟在XML声明和处理指令之间。
文档类型声明以“<!DOCTYPE>” 开始,以“]>”结束。在XML处理指令和根元素之间定义。
< ? xml version=”1.0” encoding=”GB2312” standalone=”yes” ?>
<! DOCTYPE 根元素名称 [
<! ELEMENT 子元素名称 (#PDATA)>
]>
内部DTD实例:
<?xml version="1.0“ standalone=“yes” ?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
外部DTD
外部文件类型定义存在于独立文件中,文件扩展名为“.dtd”。外部DTD的好处是:可以方便地被多个XML文档共享,只需要定义一份DTD文档,即可为多个XML文档定义语义约束。
<!DOCTYPE 根元素名 SYSTEM " DTD-URL ">
<!DOCTYPE Book SYSTEM “http://www.crazyit.org/dtd/book.dtd”>
System :关键字,指该外部DTD是私有的
DTD-URL:通过URL将外部DTD引用到XML文档中,可以是绝对地址也是可相对地址
外部DTD实例:
创建:
<?xml version="1.0“ encoding=“utf-8”?>
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
引用:
<?xml version="1.0“ standlone=“no”?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
注意:
为了让DTD能支持非西欧字符,应该为外部DTD文档指定声明,DTD声明和XML声明的语法完全相同。
公用DTD
有一种外部DTD,是由某个权威机构制定,供特定行业或公司,这种DTD又被称为公用DTD。
<!DOCTYPE 根元素名 PUBLIC " DTD-NAME “ “DTD-URL”>
注:公用DTD与外部DTD区别在于:公用DTD使用PUBLIC代替了原来的SYSTEM,并增加了DTD标识名。
DTD文档的结构
DTD文档并不是XML文档,而只是为XML定义语义约束的文档。DTD文档有如下结构:
第1行是DTD声明部分,该声明与XML声明的语法相同
0到多个注释部分,DTD注释与XML注释的语法完全相同
0到多个<!ELEMENT….>定义,每个<!ELEMENT….>定义一个XML元素
0到多个<!ATTLIST….>定义,每个<!ATTLIST….>定义一个XML元素定义了一个属性
0到多个<!ENTITY….>定义,每个<!ENTITY….>定义一个实体
0到多个<!NOTATION….>定义,每个<!NOTATION….>定义一个符号
DTD对元素声明【重点掌握】
使用ELEMENT声明XML元素的语法,语法格式如下:
<!ELEMENT 元素名 元素内容>
元素声明以“<!”开始,以“>”结束
元素声明指令“ELEMENT”为关键字,必须大写
元素名:为当前元素指定的元素名称
元素内容: 元素名后面的内容用来指定元素的内容类型,它可以分为EMPTY(空)、子元素类型,混合型、ANY(任意)和#PCDATA 5种类型。
空元素类型—EMPTY
该元素只可能有属性不能有元素内容。声明的
空元素的语法如下:
<!ELEMENT 元素名 EMPTY>
定义任意类型的元素:
ANY是DTD中使用很频繁的一个关键字,特别是对于文档根元素的声明。在定义一个DTD文档时通常很难准确地确定一个元素是否具有子元素的情况,此时一般的做法是指定该元素的子元素为ANY型(表示可以是任意的元素),这样在它之中可以包含任何数据、何声明的子元素及其数据和子元素的组合。如果需要定义某个元素的值可以是任意类型。声明的语法如下:
<!ELEMENT 元素名 ANY>
注意:
DTD必须定义XML文档中允许出现的所有元素。由此可见,虽然XML文档允许开发者自由扩展各种标签,但一旦使用DTD为其增加了语义约束,该XML文档就只能出现在DTD中定义过的元素。
定义字符串内容的元素
定义只能是字符串的元素的语法格式如下
<!ELEMENT 元素名 #PCDATA>
定义混合内容元素
某个元素既有字符串内容,又包含子元素,被称为混合内容的元素。一般来说,XML文档不推荐使用混合内容的元素。
<!ELEMENT 父元素名 (#PCDATA|子元素1|子元素2|子元素3…..)*>
注意:
#PCDATA必须放在最前面
#PCDATA和各子元素之间只能用竖线(|)分割
子元素1,子元素2和子元素3之间的竖线(|)并不是表示互斥,而只是表 示这些元素能无序的重复出现,而且次数不受限制
不要试图在各个子元素之后添加?,*。+等表示频率的修饰符
例子:
<?xml version="1.0" encoding="GB2312"?>
<!ELEMENT 喜欢的游戏 (#PCDATA | 游戏)*>
<!ELEMENT 游戏 (#PCDATA | 游戏名称 | 游戏类型)*>
<!ELEMENT 游戏名称 (#PCDATA)>
<!ELEMENT 游戏类型 (#PCDATA)>
解析:
<喜欢的游戏>是混合内容元素,可以包含字符串,也可以包含子元素<游戏>,而且可以出现多次
<游戏>也是混合内容元素,可以包含字符串,又可以包含
<游戏名称>和<游戏类型>两个元素,而且这两个元素的出现 次数也比较随意
定义子元素
元素包含子元素是XML文档中最常见的情形,DTD可以有效地定义各元素之间的父子关系,从而有效地描述整个文档结构。定义元素包含的子元素时,各个元素之间存在几种逻辑关系。
有序的子元素
用英文逗号(,)作为子元素之间的分隔符,则子元素之间必须遵守所定义的顺序。
例如:
<!ELEMENT 计算机书籍 (书名,作者,价格,简要介绍)>
互斥的子元素
互斥的子元素表明一系列子元素之间只能出现其中一次。互斥子元素使用竖线(|)分隔,以竖线(|)分隔的多个元素只能出现其中之一。
例如:
<!ELEMENT 计算机书籍 (书名|作者|价格|简要介绍)>
子元素出现的频率:
子元素的出现频率通过在元素声明后紧跟一个表示频率的特殊标记来表示,DTD中表示频率的特殊
标记有3个:
+: 表明子元素可以出现1次或多次
*: 表明子元素可以出现0次或多次
?: 表明子元素可以出现0次或1次
如果在定义子元素时,没有在子元素后指定任何表示频率的特殊标记,则表明这些子元素只能出现一次,且必须一次。
组合子元素
例如
<!-- 将 书名、作者 定义成组,该元素组可出现1~多次 -->
<!ELEMENT 计算机书籍 ((书名,作者)+,价格,简要介绍)>
无序的子元素:
从理论上讲,DTD没有专门为定义无序子元素提供语法,如果希望使用DTD来表示某个元素之内可以接受无序的子元素。
例如:
<!ELEMENT 计算机书籍 (书名|作者|价格|简要介绍)+>
DTD对属性声明【重点掌握】
属性是描述元素的额外信息的,是对元素的修饰与补充。一般来说,信息详实的XML文档都是一个特点,即元素通过属性来描述边缘信息。
在 DTD 中,属性通过 ATTLIST 声明来进行声明。一个属性声明可以声明一个元素的多个属性。
例:<! ATTLIST 元素名 属性名 属性类型 [属性限定条件] [默认值]>
“属性限定条件”和“默认值‘两部分是可选的吗,有下面几种情况
在没有指定“元素对属性的约束”时,必须为该属性指定“默认值”;
当“元素对属性的约束”是#REQUIRED时,不能为该属性指定“默认值”;
当“元素对属性的约束”是“IMPLIED”时,不能为该属性指定“默认值”;
当“元素对属性的约束”是“FIXED”时,必须为该属性指定“默认值”;
对属性的限定条件:
#REQUIRED: 必须的属性,意味着必须为该元素提供该属性
#IMPLIED: 该属性是可有可无的
#FIXED: 该属性的值是固定的,定义是必须制定固定值。使用该元素时无需为其分配该属性,XML处理器会自动为给属性增加固定值
定义属性类型
属性类型 | 含义 |
CDATA | 值为字符串数据 |
(en1|en2|..) | 此值是枚举列表中的一个值 |
ID | 该属性值必须是有效地标识符,在XML文档时唯一的 |
IDREF | 值为另外一个元素的 id属性值 |
IDREFS | 值必须引用自多个已经有的ID属性值,多个ID属性值用空格隔开 |
NMTOKEN | 值为合法的 XML 名称 |
NMTOKENS | 值为多个合法的 XML 名称的列表 |
ENTITY | 值是一个外部实体,如图片支持 |
ENTITIES | 值是一个实体列表,多个实体之间以空格隔开 |
NOTATION | 该属性值是在DTD中声明过的符号,这个是过期的,不要使用 |
字符类型
CDATA 是简单的纯文本字符类型,是最常用的类型,将简单的文本用做属性值。可以包括任何字符串,但不允许使用“<”,“>”,“&”,“””,“‘”。如果需要使用必须使用实体引用。属性值和元素内容都可以是文本类型,但是定义的方法不同
<! ATTLIST Student name CDATA #REQUIRED>
枚举类型
声明了属性的备选值列表,属性必须从该列表中选择一个值作为属性值。
例子:
创建:
<?xml version="1.0" encoding="GB2312"?>
<!ELEMENT 购物车 (肉* , 水果*)>
<!ELEMENT 肉 EMPTY>
<!ELEMENT 水果 EMPTY>
<!ATTLIST 肉 类型 (鸡肉|牛肉|猪肉|鱼肉) #REQUIRED>
<!ATTLIST 水果 类型 (苹果|梨|桔子) #REQUIRED>
引用:
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE 购物车 SYSTEM "enumerated.dtd">
<购物车>
<肉 类型="猪肉"/>
<肉 类型="鸡肉"/>
<水果 类型="苹果"/>
<水果 类型="梨"/>
</购物车>
ID、IDREF、 IDREFS类型
ID类型是一种较为严格的约束,它要求属性值必须是有效地XML标识名,而且在整个XML文件档中其值不能重复。
<?xml version="1.0" encoding="GB2312"?>
<!ELEMENT beans (bean|object)*>
<!ELEMENT bean (property*)>
<!ELEMENT object EMPTY>
<!ELEMENT property EMPTY>
<!ATTLIST bean id ID #REQUIRED> // <bean…/>必须有id属性,必须是类ID型
<!ATTLIST object id ID #REQUIRED> //<object…/>必须有id属性,必须是类ID型
<!ATTLIST property name CDATA #REQUIRED> //<property…/>必须有name属性,字符串类型
<!ATTLIST property ref IDREF #IMPLIED> //<property…/>可以有ref属性,IDREF类型
<!ATTLIST property set IDREFS #IMPLIED> //<property…/>可以有set属性,IDREFS类型
NMTOKEN和NMTOKENS类型
NMTOKEN是一个比ID类型更宽松的类型,它只要求该属性是个合法的XML标识。它也是字符串数据,但是约束比CDATA约束要严格,它要求出现的字符要少。
ENTITY和ENTITIES类型
定义ENTITY和ENTITIES类型的属性其属性值只能是未解析的实体,与外部数据相连。如图像文件,音像文件等。
例:<?xml version="1.0" encoding="gb2312"?>
<!ENTITY desc_movie SYSTEM "http://www.crazyit.org/test.wmv" >
<!ENTITY book_part1 SYSTEM "http://www.crazyit.org/part1.pdf" >
<!ENTITY book_part2 SYSTEM "http://www.crazyit.org/part2.pdf" >
<!ENTITY book_part3 SYSTEM "http://www.crazyit.org/part3.pdf" >
<!ELEMENT book (#PCDATA)>
<!-- 定义2个属性,它们的类型分别是ENTITY和ENTITIES -->
<!ATTLIST book description ENTITY "desc_movie">
<!ATTLIST book content ENTITIES #IMPLIED >
注意:
一旦声明某个属性的类型是entity或entities,就意味着该属性值只能是一个或多个未解析的实体,而不是已经解析的实体。对于未解析的实体而言,不能通过普通实体引用方式去引用,他们只能作为entity或entities类型的属性值。
定义实体
所谓实体引用就是用一个字符串代替另一个字符串。定义实体有如下作用:提高代码的服用,方便修改,维护XML文档使用某些特殊的符号,这些特殊的符号可能会使XML解析器混淆减少字符输入量,如果某个字符串特别长,而且需要经常使用,则可以定义为实体
实体的概念
实体是包含了文档片段的虚拟存储单元,可用来存储XML声明、DTD、其他形式的文本及二进制数据等。简单来讲就是一段代码或数据的代称,这个代称即为实体的名字。
当需要在文档中引用某段代码或数据时,可以引用与这段代码或数据相对应的实体名称来代替实体的具体内容。具有正确性检查功能的XML处理器在提交文档给最终应用程序之前或在显示文档以前,将先把所有不同的实体引用替换为与其对应的具体内容,从而构成一个结构完整的文档。
实体的分类
按照实体的具体内容来分类,实体可分为可解析与不可解析两类。可解析实体的具体内容为简单的字符、数字、文本块,而不可解析实体的具体内容则为图片、声音等二进制文件。
按照逻辑存储来分类,实体可分为内部实体与外部实体两类。内部实体的内容是在文档内部设定的;而外部实体则是一个外部独立的物理存储对象,如某个外部文件。按照使用的范围来分类,实体可分为一般实体与参数实体两类。一般实体都用来构成文档的具体内容,可出现在XML文档中,也可出现在DTD中;而参数实体只能出现在DTD中,不能出现在XML文档中。
定义普通实体
<! ENTITY 实体名 “实体值”>
使用实体的语法:&实体名;
定义了可在XML文档中使用的实体后,该实体既可以作为某个XML元素的字符串使用,也可以作为某个XML元素的属性值使用
定义参数实体
参数实体是在DTD文档里使用的实体,语法:
<! ENTITY % 实体名 “实体值”>
使用实体的语法:%实体名;
注:
如果需要DTD中使用参数实体,参数实体必须前向定义,再使用实体参数之前定义,不能颠倒顺序。
外部普通实体
外部实体的值不在DTD中直接指定,而是专门提供一个文件为该实体指定值----“实体值所在的文件的URL”处的文件来指定。定义外部实体的语。
法格式:<! ENTITY 实体名 SYSTEM “实体值所在文件的URI”>
<! ENTITY 实体名 PUBLIC “公用实体标识名” “实体值所在文件的URI”>
外部参数实体
类似于定义内部参数实体,语法结构如下:
<!ENTITY %实体名 SYSTEM|PUBLIC[“公用实体标识名”] “实体值所在文件的URI”>