1.XML文件约束与DTD的简单介绍
我们编写文档来约束一个XML文档的书写规范,这称之为XML约束。
常用的约束技术有:
DTD的基本概念:
document type definition 文档类型定义
DTD文件一般和XML文件配合使用,主要是为了约束XML文件。
XML文件引入DTD文件,这样XML可以自定义标签,但又受到DTD文件的约束。比如上一节使用XML描述一个班级的信息,如果我们给每一个学生定义一个<面积>
标签,语法上也是没有错误的,但是不符合语义,学生怎么能够用面积来描述呢?这时候我们就需要用到DTD文件来约束这个XML。
<?xml version="1.0" encoding="gb2312"?>
<class>
stu id=
"001">
name>杨过
</name>
sex>男
sex>
age>20
age>
面积>100
面积>
stu>
class>
1.1 DTD约束快速入门案例
基本语法:
1
!ELEMENT 元素名 类型>
我们还以班级为例,编写如下DTD文件,myClass.dtd:
1
2
3
4
5
!ELEMENT 班级 (学生+)>
!ELEMENT 学生 (名字,年龄,介绍)>
!ELEMENT 名字 (#
PCDATA)>
!ELEMENT 年龄 (#
!ELEMENT 介绍 (#
PCDATA)>
第一行表示根元素为班级,并且有学生这个子元素,子元素为1或者多个。
第二行表示学生的子元素为名字,年龄,介绍
名字下面没有子元素了,那么#PCDATA表示名字里面可以放任意文本。
年龄和介绍也是类似。
编写myClass.xml文件并引入DTD文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE 班级 SYSTEM "myClass.dtd">
班级>
学生>
名字>周小星
名字>
年龄>23
年龄>
介绍>学习刻苦
介绍>
学生>
名字>林晓
名字>
年龄>25
介绍>是一个好学生
学生>
班级>
引入中写的:SYSTEM,表示当前的DTD文件是本地的
如果写的是PUBLIC,则表示引入的DTD文件是来自于网络的.
这时候引入的DTD文件是没有产生作用的,如果我们在学生元素中添加子元素<面积>
,打开这个XML文件,浏览器依然不会报错。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
介绍>
面积>100平米
我们需要编程校验XML文档的正确性。
IE5以上的浏览器内置了XML解析工具:Microsoft.XMLDOM,开发人员可以编写JavaScript代码,利用这个解析工具装载XML文件,并对XML文件进行DTD验证。
我们编写myXmlTools.html来对这个XML进行校验,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
html>
head>
script language=
"javascript">
@H_301_487@var xmldoc = @H_
301_487@new ActiveXObject(
"Microsoft.XMLDOM"); xmldoc.validateOnParse =
"true"; xmldoc.load(
"myClass.xml"); document.writeln(
"错误信息:"+xmldoc.parseError.reason+
"<br>"); document.writeln(
"错误行号:"+xmldoc.parseError.line);
script>
head>
body>
body>
html>
用IE浏览器打开这个html文件,可以看到运行结果:
可以看到第9行正是我们添加的<面积>
这一行。
2.DTD细节
2.1 DTD文档的声明及引用
1.内部DTD文档
1
<!DOCTYPE 根元素 [定义内容]>
2.外部DTD文档
引入外部的DTD文档分为两种:
(1)当引用的DTD文件是本地文件的时候,用SYSTEM标识,并写上”DTD的文件路径”,如下:
<!DOCTYPE 根元素 SYSTEM "DTD文件路径">
(2)如果引用的DTD文件是一个公共的文件时,采用PUBLIC标识,如下方式:
<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文件的URL">
比如下例:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems,Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
2.2 DTD基本语法:
!ELEMENT NAME CONTENT>
其中:
- ELEMENT是关键字,是不能修改的
- NAME表示元素名称
- CONTENT是元素类型,必须要大写!CONTENT的内容有三种写法:
(1)EMPTY——表示该元素不能包含子元素和文本,但可以有属性。
(2)ANY——表示该元素可以包含任何在该DTD中定义的元素内容
(3)#PCDATA——可以包含任何字符数据,但是不能在其中包含任何子元素
2.3 DTD元素的组合类型:
DTD中这样规定:
!ELEMENT 家庭(人+,家电*)>
这个DTD规定了家庭元素中可以有1到多个”人”这个子元素,也可以有0到多个”家电”这个子元素。其中的加号”+”和星号”*”的含义与正则表达式中的含义一致。
XML这样写:
家庭>
人 名字="张晓明" 性别="男" 年龄="25"/>
"李小钢" 性别="36" 爱好="作个教育家和伟人"/>
家电 名称="彩电" 数量="3"/>
家庭>
关于组合类型,有下述的的修饰符可以使用:
符号 |
用途 |
示例 |
示例说明 |
() |
用来给元素分组 |
(古龙|金庸),(王朔|余杰) |
分成两组 |
| |
在列出的对象中选择一个 |
(男人|女人) |
表示男人或者女人必须出现,两者至少选其一 |
+ |
该对象必须出现一次或者多次 |
(成员+) |
表示成员必须出现,而却可以出现多个成员 |
* |
该对象允许出现0次或者多次 |
(爱好*) |
爱好可以出现两次到多次 |
? |
该对象必须出现0次或者1次 |
(菜鸟?) |
菜鸟可以出现,也可以不出现,如果出现的话,最多只能出现一次 |
, |
对象必须按指定的顺序出现 |
(西瓜,苹果,香蕉) |
表示西瓜、苹果、香蕉必须出现,并且按这个顺序出现 |
2.4 属性定义
DTD中属性的定义是这样的:
1
2
3
4
!ATTLIST 元素
名称 属性名称 类型
属性特点
属性名称 类型
属性特点
...... >
其中,属性的类型有下面5种:
(1) CDATA
(2) ID
(3) IDREF/IDREFS
(4) Enumerated
(5) ENTITY/ENTITIES
属性的特点有如下4种:
(1) #required,表示这个属性必须给,不给就报错
(2) #IMPLIED,表示这个属性可以给也可以不给
(3) #FIXED value,表示这个属性必须给一个固定的value值
(4) Default value,表示这个属性如果没有值,就分配一个默认的value值
比如,我们想在学生这个子元素上加上地址这个属性,而且这个属性是必须的,示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
<?xml version="1.0" encoding="utf-8"?>
学生 地址=
"香港">
"澳门">
这个时候相应的DTD文件也要更新,不然就会报错,如下:
1
2
3
4
5
6
7
8
!ATTLIST 学生 地址 CDATA #required >
PCDATA)>
2.4.1 对于属性类型的详细解释
(1)属性类型-CDATA,表示属性值可以是任何字符(包括中文和数字)
1
2
3
!ATTLIST 木偶 姓名
required >
木偶 姓名="匹诺曹"/>
"PiNuocao"/>
"123"/>
(2)属性类型-ID,表明该属性的取值必须是唯一的,但是属性的值不能是以数字开头!
!ELEMENT 公司职员 ANY>
!ATTLIST 公司职员 编号 ID #required 姓名 required >
1
2
公司职员 编号=
"Z001" 姓名=
"张三"/>
"Z002" 姓名=
"李思"/>
(3)属性类型-IDREF/IDREFS
- IDREF属性的值指向文档中其它地方声明的ID类型的值
- IDREFS同IDREF,但是可以具有由空格分开的多个引用。
1
2
3
4
5
6
7
!ELEMENT 家庭(人+)>
!ELEMENT 人
EMPTY>
!ATTLIST 人
relID required paraentID IDREFS #
IMPLIED name required >
人 relID="P_1" name="爸爸"/>
"P_2" "妈妈"/>
"P_3" parentID="P_1 P_2" "儿子"/>
(4)属性类型-Enumerated,事先定义好一些值,属性的值必须在所列出的值的范围内。
1
2
3
4
5
6
!ATTLIST person 婚姻状态 (single|married|divorced|widowed) #IMPLIED >
person 性别 (男|女) #required >
(5)属性类型-ENTITY,实体
实体定义:
- 实体用于为一段内容创建一个别名,以后在XML文档中就可以使用别名引用这段内容了。
- 在DTD定义中,一条!ENTITY语句用于定义一个实体。
- 实体可分为两种类型:引用实体和参数实体。引用实体是被XML文档应用的,而参数实体是被DTD文件本身应用的。
①引用实体:
- 引用实体主要在XML文档中被应用
语法格式如下,引用实体的定义内容最好放在DTD文件的最后。
!ENTITY 实体名称 "实体内容">
引用方式:&实体名称; 末尾要带上分号,这个引用将直接转变成实体内容
举例如下:
!ENTITY copyright "I am a programmer">
....
©right;
②参数实体:
参数实体被DTD文件自身使用
语法格式为:
!ENTITY % 实体名称 "实体内容">
引用方式为:%实体名称
举例:
!ENTITY % TAG_NAME "姓名|EMAIL|电话|地址">
!ELEMENT 个人信息 (%TAG_NAME;|生日)>
!ELEMENT 客户信息 (%TAG_NAME;|公司名)>
3.DTD实际案例
学习DTD的目标在于:
(1)要求我们能够看得懂DTD文件,
(2)我们可以根据给出的DTD写出对应的XML文件
下面我们看一个案例,下述的DTD文件是从W3School在线教程中的DTD案例中拿过来的,细看每一行,我们都应该能够看得懂。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
AUTHOR "
John Doe">
COMPANY "
JD Power Tools,
Inc.">
EMAIL "
jd@
jd-tools.com">
CATALOG (
PRODUCT+)>
PRODUCT (
SPECIFICATIONS+,102);">OPTIONS?,102);">PRICE+,102);">NOTES?)>
PRODUCT CATEGORY (
HandTool|
Table|
Shop-Professional) "
HandTool"
PARTNUM PLANT (
Pittsburgh|
Milwaukee|
Chicago) "
Chicago"
INVENTORY (
InStock|
Backordered|
Discontinued) "
InStock">
SPECIFICATIONS (#
SPECIFICATIONS WEIGHT POWER IMPLIED>
OPTIONS (#
OPTIONS FINISH (
Metal|
Polished|
Matte) "
Matte"
ADAPTER (
Included|
Optional|
NotApplicable) "
Included"
CASE (
HardShell|
Soft|
HardShell">
PRICE (#
PRICE MSRP WHOLESALE STREET SHIPPING NOTES (#
然后我们可以根据该DTD编写如下最简单的XML文件:
1
2
3
4
5
6
7
8
9
<!DOCTYPE CATALOG SYSTEM "product.dtd">
CATALOG>
PRODUCT NAME=
"康帅傅矿泉水" CATEGORY=
"Table" PARTNUM=
"12" PLANT=
"Chicago">
SPECIFICATIONS WEIGHT=
"20" POWER=
"18">这里是细节
SPECIFICATIONS>
PRICE>25
PRICE>
PRICE>28
PRICE>
PRODUCT>
CATALOG>
然后我们用Microsoft.XMLDOM校验该XML,会发现没有任何错误。但是要注意编码。