02.XML技术
一、XML简介
XML指可扩展标记语言(eXtensible Markup Language),是用来传输和存储数据的,是一种通用的数据交换格式,可以使数据在不同应用程序间交换,具有很强的可扩展性。
- 可扩展,是指用户可以按照XML规则自定义标记,英文、中文皆可。
- 标记语言,很类似于HTML。XML不是代替HTML的,是对HTML的补充。
W3C在1998年2月发布1.0版本;W3C在2004年2月发布1.1版本,但因为1.1版本不能向下兼容1.0版本,所以1.1没有人用。同时,在2004年2月W3C又发布了1.0版本的第三版。我们要学习的还是1.0版本!!!
XML是不作为的,就是说“XML不会做任何事情”。XML被设计用来结构化、存储和传输信息。
XML的作用:
- 数据存储:可作为小型关系数据库,表示数据之间的关系,用来当数据库存储数据。
- 数据交换:不同语言之间、程序之间、系统之间交换数据。
- 程序的配置文件:这也是大家使用XML最常见的目的。比如,连接数据库时,在xml配置文件中定义数据库连接地址、用户名和密码,如果修改数据库的信息,不需要修改程序源代码,只要修改配置文件就可以了。
XML与HTML的比较:
- HTML是用来显示数据,其焦点是数据的外观;XML是用来传输和存储数据,其焦点是数据的内容。
- HTML是不区分大小写的,而XML是严格区分大小写的。
- HTML可以有多个根元素,而格式良好的XML有且只能有一个根元素。
- HTML中属性值的引号是可用可不用的,XML中的属性值必须放在引号中。
- HTML中空格是自动过滤的,而XML中空格则不会自动删除。
- HTML中的标记是预定义的,而XML中的标记可以根据需要自己定义,并且可扩展。
我们举个例子来看一下我们身边的XML:
中国有很多省份,每个省份下又有很多城市,这些省市之间的关联关系可以通过一张树状结构图来描述,如图所示。
图中直观地描述了中国与所辖省、市之间的层次关系。但是对于程序而言,解析图片内容非常困难,这时,采用XML文件保存这种具有树状结构的数据是最好的选择。
下面的代码就可以描述出上图所示的关系。
<?xml version="1.0" encoding="UTF-8"?> <中国> <河北> <城市>张家口</城市> <城市>石家庄</城市> </河北> <山西> <城市>太原</城市> <城市>大同</城市> </山西> </中国>
二、XML语法
一个基本的XML文档通常是由文档声明(序言)和文档元素两部分组成。下面我们来分别介绍一下。
1、文档声明
在一个XML文档中,必须包含一个XML文档声明,并且必须位于文档的第一行。语法格式:
<?xml 版本信息 [编码信息] [文档独立性信息]?>
文档声明以符号“<?”开头,以符号“?>”结束,中间可以声明版本信息、编码信息以及文档独立性信息。
需要注意的是,在“<”和“?”之间、“?”和“>”之间以及第一个“?”和xml之间不能有空格;
另外,中括号([])括起来的部分是可选的。完整的代码如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
- version属性表示XML的版本。目前,最常用的XML版本是1.0。
- encoding属性用来说明XML文档所使用的编码方式,默认情况下,XML文档使用的是UTF-8编码方式。
- standalone属性用来声明这个文档是否是独立的文档,默认情况下,standalone属性的值为no,表示文档依赖于外部文档。
这里提到的依赖,是指文档需要DTD文件验证其中的标识是否有效,或者需要CSS(层叠样式表,HTML样式表)、XSL(可扩展样式表语言,XML样式表)文件来控制显示外观。
编码详解:
我们来编写一段代码:
<?xml version="1.0" encoding="UTF-8"?> <note> <from>Tom</from> <to>Jerry</to> <message>你好</message> </note>
在XML文档中不指定任何编码属性时,我们使用Windows记事本保存XML要注意:
如果XML 中包含非ASCII字符(中文、法文等),我们将文件“另存为”编码ANSI后,使用IE浏览器浏览将会出错。
ANSI编码是一种对ASCII码的拓展,ASCII能表示128个字符,ANSI能表示256个字符。
如果将文件“另存为”编码UTF-8或Unicode后,使用IE浏览器浏览将不会出错。
如果将文件“另存为”编码UTF-8或Unicode后,而XML的encoding指定为"GB2312",使用IE浏览器浏览汉字将会显示乱码。
所以XML文档的保存编码一定要和文档声明中的encoding一致起来,最好都使用UTF-8。
【补充知识】ASCII、ANSI、GB2312、GBK、Unicode、UTF-8编码:
① ASCII:首先明白计算机能识别的都是二进制010101010这种代码,但是这些010101010人是难以看懂的,于是美国人就用ASCII码(American Standard Code for Information Interchange,美国信息交换标准码)制作了一张表,里面包含从a、b、c.......@%$等128个字符。但是,美国用的字母,德国、法国,还有咱们中国汉字几万个,一张表存不下。
② ANSI:扩展了ASCII,能表示256个字符。
③ GB2312、GBK:汉字内码扩展规范(GBK即“国标”、“扩展”汉语拼音的第一个字母)。GBK是在GB2312标准基础上的内码扩展规范。GBK编码规定,计算机不能在每次都只读一个字节(00000000,8位表示一个字节),而是要先看看第一位是不是为0,要是为0的话,就当作ASCII码来读入一个字节,不然的话就读入两个字节(汉字太多一个字节存不下,读入两个字节表示汉字,就查GBK)。
④ Unicode:那么每个国家一个编码表,相互通信的时候由于解码方式不同就会导致乱码(用ASCII发邮件,计算机查ASCII表转换成对应0101010二进制发送出去,接收的人用GBK解码,将0101010查GBK表肯定就查不到啊)。于是,国际组织就发明了一套公用的表Unicode编码,将所有国家,所有字符都收进去了,从0一直到100多万(用三个字节表示)。
由于历史方面的原因,我们经常说的Unicode 通常就是 UTF-16。
⑤ UTF-8:虽然有了世界通用的表Unicode,但是有人就发现这有点浪费资源啊。每次让计算机读取三个字节,然后参照Unicode表解码,那么像a、b...0、1、2...这些一个字节就够了的就太浪费了。于是UTF-8、UTF-16、UTF-32这些编码方案就出现了。UTF-16是用两个字节来编码所有的字符,UTF-32则选择用4个字节来编码,UTF-8为了节省资源,采用变长编码,编码长度从1个字节到4个字节不等。由于互联网大部分是1个字节(代码很多英文的),所以最后大家选择用的最多的还是UTF-8。
2、元素定义
在XML文档中,主体内容都是由元素(Element)组成的。元素一般是由开始标记、属性、元素内容和结束标记构成,具体示例如下:
<城市 类型="直辖市">北京</城市>
上面的示例中,“<城市>”和“</城市>”就是XML文档中的标记,标记的名称也就是元素的名称。在一个元素中可以嵌套若干子元素。
如果一个元素没有嵌套在其它元素内,则这个元素称为根元素。根元素是XML文档定义的第一个元素。
如果一个元素中没有嵌套子元素,也没有包含文本内容,则这样的元素称为空元素,空元素可以不使用结束标记,但必须在开始标记的结束符“>”前增加一个正斜杠“/”来说明该元素是个空元素,例如:<img></img>可以简写成<img/>。
在XML文档中,元素的名称可以包含字母、数字以及其他一些可见的字符,但是在命名XML元素时,应该遵守以下规范。
(1)XML是强标记语言,区分大小写,例如: <P>和<p>是两个不同的标记。
(2)元素名称中,不能包含空格、冒号、分号、逗号和尖括号等,元素不能以数字开头,否则XML文档会报错。<7eleven />
(3)建议不要使用“.”,因为在很多程序语言中,“.”用于引用对象的属性。<user.id />
(4)建议不要用减号(-),而用下划线(_)代替,以避免与表达式中的减号(-)运算符发生冲突。<user-name />
(5)建议元素名称不要以字符组合xml(或XML、或Xml等)开头。<xmlfile />
(6)建议元素名称的大小写尽量采用同一标准,要么全部大写,要么全部小写。
(7)元素名称可以使用非英文字符,例如中文,但有些软件可能不支持英文字符以外的字符,在使用时应考虑这种情况。
3、属性定义
在XML文档中,可以为元素定义属性。属性是对元素的进一步描述和说明。在一个元素中,可以有多个属性,并且每个属性都有自己的名称和取值,具体示例如下:
<售价 单位="元">68</售价>
在上面的示例中,<售价>元素中定义了一个属性“单位”。需要注意的是,在XML文档中,属性的命名规范同元素相同,属性值必须要用双引号(")或者单引号(')引起来,否则被视为错误。
4、注释
如果想在XML文档中插入一些附加信息,比如作者姓名、地址或电话等信息,或者想暂时屏蔽某些XML语句,这时,可以通过注释的方式来实现,被注释的内容会被程序忽略而不被解析和处理。XML注释和HTML注释写法基本一致,具体语法格式如下所示:
<!-- 注释信息 -->
- 注释不能嵌套: <!-- <!-- --> -->
- 注释也不能放到第一行,第一行第一列必须放文档声明。
5、特殊字符处理(转义字符)
和HTML类似
预定义实体 |
特殊字符 |
含义 |
< |
< |
小于 |
> |
> |
大于 |
& |
& |
和号 |
' |
' |
单引号 |
" |
" |
引号 |
6、CDATA区(格式记住)
Character Data,字符数据,指一段不想被程序解析的原始数据,这里面的数据会原封不动的显示出来,不会被转义。
<![CDATA[ 一段代码 ]]>
例如,想直接存储以下代码,而不使用转义字符。if(a<b && b<c && d>f) {}
代码: <![CDATA[ if(a<b && b<c && d>f) {} ]]>
把特殊字符,当做文本内容,而不是标签。
注意:
① CDATA 部分不能包含字符串 "]]>",也不允许嵌套的 CDATA 部分。
② 标记 CDATA 部分结尾的 "]]>" 不能包含空格或折行。
三、约束
在现实生活中,如果一篇文章的语法正确,但内容包含违法言论或逻辑错误,这样的文章是不允许发表的。
同样,在编写XML文档时,其内容必须满足某些条件的限制,例如以下代码:
<?xml version="1.0" encoding="UTF-8"?> <书架> <书> <书名>JavaWeb程序开发入门</书名> <作者>传智播客</作者> <售价>60</售价> <售价>30</售价> </书> </书架>
在上面的示例中,尽管这个XML文档结构是正确的,用IE浏览器打开它也不会出现任何问题,但是,由于XML文档中的标记是可以随意定义的,同一本书出现了两种售价,如果仅根据元素名称区分哪个是原价,哪个是会员价,这是很难实现的。为此,在XML文档中,定义了一套规则来对文档中的内容进行约束,这套约束称为XML约束。
对XML文档进行约束时,同样需要遵守一定的语法规则,这种语法规则就形成了XML约束语言。目前,最常用的两种约束语言是DTD约束和Schema约束。
四、DTD约束
DTD(Document Type Definition,文档类型定义)的作用是定义XML文档的合法构建模块,使用一系列的合法元素来定义文档结构。在一个DTD文件中,可以包含元素的定义、元素之间关系的定义、元素属性的定义以及实体和记号的定义。
1、DTD定义
我们来看实例代码book.xml
<?xml version="1.0" encoding="UTF-8"?> <书架> <书> <书名>JavaWeb程序开发入门</书名> <作者>传智播客</作者> <售价>60</售价> </书> <书> <书名>JavaWeb从入门到精通</书名> <作者>明日科技</作者> <售价>80</售价> </书> </书架>
与之对应的DTD约束文档book.dtd,代码如下:
<!ELEMENT 书架 (书+)> <!ELEMENT 书 (书名,作者,售价)> <!ELEMENT 书名 (#PCDATA)> <!ELEMENT 作者 (#PCDATA)> <!ELEMENT 售价 (#PCDATA)>
对book.dtd文档所规定的约束详细解释如下:
(1)在第1行中,使用<!ELEMENT…>语句定义了一个元素,其中“书架”是元素的名称,“(书+)”表示书架元素中有一个或者多个书元素,字符+用来表示它所修饰的成分必须出现一次或者多次。
(2)在第2行中,“书”是元素名称,“(书名,作者,售价)”表示元素书包含书名、作者、售价这三个子元素,并且这些子元素要按照顺序依次出现。
(3)在第3-5行中,“书名”、“作者”和“售价”都是元素名称,“(#PCDATA)"表示元素的内容是可解析字符数据(Parsed Character Data),就是普通文本字符串。
那么,创建DTD文件步骤:
(1)看XML中有几个元素,在DTD中就写几个<!ELEMENT>。
(2)判断元素是简单元素还是复杂元素:
- 简单元素:没有子元素,语法如下: <!ELEMENT 元素名称 (#PCDATA)>
- 复杂元素:有子元素的元素,语法如下: <!ELEMENT 元素名称 (子元素+)> 或 <!ELEMENT 元素名称 (子元素,子元素,……)>
2、DTD的引入
有了DTD文件,如果想使用DTD文件来约束XML文档,必须在XML文档中引入DTD文件。
(1)外部引入
在XML文档中引入外部DTD文件有两种方式,具体如下:
<!DOCTYPE 根元素名称 SYSTEM "外部DTD文件URI"> <!DOCTYPE 根元素名称 PUBLIC "DTD名称" "外部DTD文件URI">
在上述两种引入DTD文件的方式中:
第一种方式用来引用本地的DTD文件,“外部DTD文件的URI”是指DTD文件的存放位置,它可以是相对于XML文档的相对路径,也可以是一个绝对路径;
第二种方式用来引用网络上公共的DTD文件,“外部DTD文件的URI”是指,Internet上的一个绝对URL地址。
如果希望引入一个公共的DTD文件,则需要在DOCTYPE声明语句中使用PUBLIC属性,具体示例如下:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
其中"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"是DTD名称,它用于说明DTD符合的标准、所有者的名称以及对DTD描述的文件进行说明,虽然DTD名称看上去比较复杂,但这完全是由DTD文件发布者去考虑的事情,XML文件的编写者只要把DTD文件发布者事先定义好的DTD标识名称进行复制就可以了。
我们看看之前学习HTML的时候,HTML文档的第一行:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
这里也是一个公共DTD的引入。
引入DTD的book.xml代码如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE 书架 SYSTEM "book.dtd"> <书架> …… </书架>
(2)内部嵌入
在XML中直接嵌入DTD定义语句的完整语法格式如下所示:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE 根元素名 [ DTD定义语句 …… ]>
上面的book.xml代码可以改写为:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE 书架 [ <!ELEMENT 书架 (书+)> <!ELEMENT 书 (书名,作者,售价)> <!ELEMENT 书名 (#PCDATA)> <!ELEMENT 作者 (#PCDATA)> <!ELEMENT 售价 (#PCDATA)> ]> <书架> …… </书架>
不管是哪种方式,通过IE浏览器浏览,我们发现即使XML不符合DTD约束,浏览时也一样可以浏览出来。因为我们使用浏览器打开XML文件,浏览器只负责校验XML的语法,不负责校验约束。
如果想要校验约束,我们需要使用工具(Eclipse)。
我们打开Eclipse,然后创建一个Java工程。在src文件夹下编写book.xml和book.dtd文件。这时,如果XML文件的编写不符合DTD约束,Eclipse就会给我们错误提示。
外部引入和内部嵌入的特点:
① 如果一个DTD文件需要被多个XML文件引用,为了避免在每个XML中都添加相同的一段DTD定义语句,我们可以将其放在一个单独的DTD文件中,采用外部引入的方式对XML文档进行管理。这样不仅便于管理和维护DTD定义,还可以使多个XML文档共享使用一个DTD文件。
② 内部嵌入比较直观,DTD约束和XML元素使用都在同一个XML文件中,编写代码、编辑DTD约束比较方便。
3、DTD语法
DTD的结构一般由元素类型定义、属性定义、实体定义、记号(notation)定义等构成,一个典型的DTD会把将来要创建的XML文档的元素结构、属性类型、实体引用等预先进行定义。
(1)元素定义
元素(标记)是XML文档的基本组成部分,在DTD定义中,每一条<!ELEMENT…>语句用于定义一个元素,其基本的语法格式如下所示:
<!ELEMENT 元素名称 元素内容>
在上面的语法格式中,包含了“元素名称”和“元素内容”。
- 元素名称:是自定义的名称,它用于定义被约束XML文档中的元素(标记、标签);
- 元素内容:是对元素包含内容的声明,包括数据类型和符号两部分。XML的元素分为简单元素和复杂元素两种,DTD中元素的定义共有5种形式,其中前三种是简单元素的类型:
① (#PCDATA):普通字符串,PCDATA是可解析字符数据(Parsed Character Data),要有括号()。
② EMPTY:空元素,既不包含字符数据,也不包含子元素。如果在文档中元素本身已经表明了明确的含义,就可以在DTD中用关键字EMPTY表明空元素。例如, <!ELEMENT br EMPTY> ,其中br是一个没有内容的空元素。不用括号()。
③ ANY:任何元素,表示该元素可以包含任何的字符数据和子元素。例如, <!ELEMENT 联系人 ANY> 表示联系人可以包含任何形式的内容。但在实际开发中,应该尽量避免使用ANY,因为除了根元素外,其他使用ANY的元素都将失去DTD对XML文档的约束效果。不用括号()。
④ 子元素:可以包含其他元素。通常用一对括号()将元素中要嵌套的一组子元素括起来,中间使用逗号“,”分隔,表示这些子元素按照这个顺序在XML中出现。例如, <!ELEMENT 书 (书名,作者,售价)> 表示元素书中要嵌套书名、作者、售价子元素,这些子元素只能出现一次,并且按照这个顺序编写。
如果一个子元素要出现多次,我们可以使用一些符号来表示次数:
+(一次或者多次),?(零次或者一次)、*(零次、一次或者多次)
例如, <!ELEMENT 书 (书名+,作者?,售价*)>
还有一个符号非常重要----竖线“|”,表示“或”的意思,如果几个子元素之间使用竖线“|”隔开,表示这几个子元素只能出现其中的任意一个。例如,<!ELEMENT 书 (书名|作者|售价)>。
⑤ 混合内容:既可以包含字符数据,也可以包含子元素。混合内容必须被定义零个或多个,例如, <!ELEMENT 书 (#PCDATA|书名)*> 表示书中嵌套的子元素书名包含零个或多个,并且书名是字符串文本格式。要有括号()。
(2)属性定义
在定义元素的同时,还可以为元素定义属性。DTD属性定义的基本语法格式:
<!ATTLIST 元素名称 属性名称1 属性类型 设置说明 属性名称2 属性类型 设置说明 …… >
在上面属性定义的语法格式中,“元素名称”是属性所属元素的名字,“属性名称”是属性的名称,“属性类型”则是用来指定该属性是属于哪种类型,“设置说明”用来说明该属性是否必须出现。关于“属性类型”和“设置说明”的相关讲解,具体如下。
① 属性类型
在DTD中定义元素的属性时,有10种属性类型可以选择,常见的几种属性类型介绍如下。
序号 |
属性类型 |
含义 |
1 |
CDATA |
纯文本字符串。如果出现特殊字符,需要转义。 |
2 |
枚举类型 |
枚举列表中的一个值,用竖线“|”隔开,每次只能选择一个。 |
3 |
ID |
唯一的id,只能是字母或下划线开始。 |
4 |
IDREF |
另外一个元素的id,【reference:引用,参考】 |
5 |
IDREFS |
其他id的列表,中间用空格隔开 |
6 |
NMTOKEN |
合法的XML名称,Name Token,名称记号,它表示由一个或多个字母、数字、句点、连字号或下划线所组成的一个名称。当encoding编码是中文码时,可以使用中文。 |
7 |
NMTOKENS |
合法的XML名称的列表,中间用空格隔开 |
8 |
NOTATION |
记号的名称(后面还有单独讲解) |
9 |
ENTITY |
一个已定义的实体,预定义实体/自定义实体(后面还有单独讲解) |
10 |
ENTITIES |
一个已定义的实体的列表 |
② 设置说明
定义元素的属性时,有4种设置说明可以选择,具体如下:
序号 |
设置说明 |
含义 |
1 |
#REQUIRED |
属性必须存在 |
2 |
#IMPLIED |
属性可有可无 |
3 |
#FIXED value |
表示一个固定值,#FIXED "ABC",表示属性必须是这个固定值"ABC" |
4 |
默认值 |
表示一个默认值,如果元素不写该属性,该属性自动被设置为默认值。如果写了该属性,就是用属性的实际值。 |
例如:
① DTD定义: <!ATTLIST 作者 分类 (AA|BB|CC) #REQUIRED>
XML文件: <作者 分类="AA">传智播客</作者>
② DTD定义: <!ATTLIST 书名 编号 ID #REQUIRED>
XML文件: <书名 编号="a1">JavaWeb程序开发入门</书名>
③ DTD定义: <!ATTLIST 售价 单位 CDATA #FIXED "元">
XML文件: <售价 单位="元">60</售价>
④ CDATA 案例----“家庭”
<?xml version="1.0" encoding="UTF-8"?> <!--包含两个元素,人至少出现1次,家电可以出现0/1/多次,这两个元素都被定义为空类型 --> <!DOCTYPE 家庭 [ <!ELEMENT 家庭 (人+,家电*)> <!ELEMENT 人 EMPTY> <!ELEMENT 家电 EMPTY> <!--开始定义属性--> <!ATTLIST 人 名字 CDATA #REQUIRED 年龄 CDATA #REQUIRED 爱好 CDATA #IMPLIED> <!ATTLIST 家电 名称 CDATA #REQUIRED 数量 CDATA #REQUIRED 说明 CDATA #IMPLIED> ]> <!--DTD部分结束 --> <家庭> <人 名字="张三" 年龄="25" /> <人 名字="李四" 年龄="38" 爱好="踢足球" /> <家电 名称="彩电" 数量="三" /> </家庭>
⑤ ID案例----“公司”
<?xml version="1.0" encoding="UTF-8"?> <!--一个内部DTD的示例,首先写根元素“公司” --> <!DOCTYPE 公司 [ <!ELEMENT 公司 ANY> <!ELEMENT 公司职员 (#PCDATA)> <!ATTLIST 公司职员 编号 ID #REQUIRED 姓名 CDATA #REQUIRED > ]> <!--DTD部分结束 --> <!--编号被定义为ID,因此是不可重复的 --> <公司> <公司职员 姓名="张三" 编号="Z001" /> <公司职员 姓名="李四" 编号="Z002" /> </公司>
⑥ IDREF/IDREFS案例----“家庭”
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE 家庭[ <!ELEMENT 家庭 (人+)> <!ELEMENT 人 EMPTY> <!ATTLIST 人 relID ID #REQUIRED parentID IDREFS #IMPLIED name CDATA #REQUIRED> ]> <!--DTD部分结束 --> <家庭> <人 relID="P_1" name="爸爸" /> <人 relID="P_2" name="妈妈" /> <!-- 属性值引用已经定义的ID值,方法是把那个元素的ID标识符作为该元素的取值,不同的引用之间用空格分开 --> <人 relID="P_3" parentID="P_1 P_2" name="儿子" /> </家庭>
⑦ NMTOKEN案例----“八阵图”
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE poems [ <!ELEMENT poems (title, content)> <!ELEMENT title (#PCDATA)> <!--定义属性,NMTOKEN说明该属性由一个或多个字母、数字、句点、连字号或下划线所组成的一个名称,当encoding编码是中文码时,可以使用中文--> <!ATTLIST title author NMTOKEN #REQUIRED> <!ELEMENT content (#PCDATA)> ]> <poems> <title author="杜甫">八阵图</title> <content> 功盖三分国,名成八阵图, 江流石不转,遗恨失吞吴. </content> </poems>
注意,如果是NMTOKENS,属性值能由多个NMTOKEN构成,每个NMTOKEN之间用空格隔开。
案例二:电视节目表 DTD
<!DOCTYPE TVSCHEDULE [ <!ELEMENT TVSCHEDULE (CHANNEL+)> <!ELEMENT CHANNEL (BANNER,DAY+)> <!ELEMENT BANNER (#PCDATA)> <!ELEMENT DAY (DATE,(HOLIDAY|PROGRAMSLOT+)+)> <!ELEMENT HOLIDAY (#PCDATA)> <!ELEMENT DATE (#PCDATA)> <!ELEMENT PROGRAMSLOT (TIME,TITLE,DESCRIPTION?)> <!ELEMENT TIME (#PCDATA)> <!ELEMENT TITLE (#PCDATA)> <!ELEMENT DESCRIPTION (#PCDATA)> <!ATTLIST TVSCHEDULE NAME CDATA #REQUIRED> <!ATTLIST CHANNEL CHAN CDATA #REQUIRED> <!ATTLIST PROGRAMSLOT VTR CDATA #IMPLIED> <!ATTLIST TITLE RATING CDATA #IMPLIED> <!ATTLIST TITLE LANGUAGE CDATA #IMPLIED> ]>
根据上面提供的DTD文档定义格式,写出XML文件内容。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE TVSCHEDULE [ …… ]> <TVSCHEDULE NAME="CCTV"> <CHANNEL CHAN="CCTV-1"> <BANNER>XXXX</BANNER> <DAY> <DATE>2019-03-11</DATE> <HOLIDAY>XXXX</HOLIDAY> </DAY> <DAY> <DATE>2019-03-12</DATE> <PROGRAMSLOT VTR="XXXX"> <TIME>1900</TIME> <TITLE LANGUAGE="TTTT">XXXX</TITLE> <DESCRIPTION>XXXX</DESCRIPTION> </PROGRAMSLOT> </DAY> </CHANNEL> </TVSCHEDULE>
(3)实体定义
有时候需要在各个文档中调用同样的内容,例如公司名称、版权声明等,为了避免重复输入这些内容。可以通过<!ENTITY……>语句定义一个表示这些内容的实体,然后在各个文档中引用实体名代替它所表示的内容。类似于定义了一个常量,在其他地方多次使用这个常量。
实体可分为两种类型,分别是引用实体和参数实体。
① 引用实体
引用实体的定义格式有两种:
<!ENTITY 实体名称 "实体内容"> <!ENTITY 实体名称 SYSTEM "外部XML文档的URL">
使用引用实体的方式为:
&实体名称;
写法非常类似于转义字符的使用:空格
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE 书架 [ <!ELEMENT 书架 (书+)> <!ELEMENT 书 (书名,作者,售价)> <!ELEMENT 书名 (#PCDATA)> <!ELEMENT 作者 (#PCDATA)> <!ELEMENT 售价 (#PCDATA)> <!ENTITY zhs "张三"> ]> <书架> <书> <书名>JavaWeb程序开发入门</书名> <作者>&zhs;</作者> <售价>60</售价> </书> <书> <书名>JavaWeb从入门到精通</书名> <作者>明日科技</作者> <售价>80</售价> </书> </书架>
② 参数实体
参数实体只能被DTD文件自身使用,语法格式:
<!ENTITY % 实体名称 "实体内容">
注意:ENTITY、%、实体名称、"实体内容"之间各有一个空格。
引用参数实体的方式为:
%实体名称;
在实体名称之前增加百分号,在其后增加分号。百分号与实体名称之间没有空格。
来看一下下面的DTD定义:
<!ENTITY % na "书名,作者"> <!ELEMENT 书架 (书+)> <!ELEMENT 书 (%na;,售价)> <!ELEMENT 书名 (#PCDATA)> <!ELEMENT 作者 (#PCDATA)> <!ELEMENT 售价 (#PCDATA)>
在第三行中引用了第一行中定义的参数实体na来表示"书名,作者"。
注意:内部 DTD 子集中的标记声明内不允许使用参数实体引用,只能在外部DTD文件中使用参数实体。
参数实体不仅可以简化元素中定义的相同内容,还可以简化属性的定义,具体实例参见课本27页。
(4)NOTATION记号定义(或符号定义)
在现实中,很多数据是无法用XML表示的,例如:声音、图像、影像、程序等,对于这些数据,XML处理器通常都不支持。
我们就需要使用记号声明,为非XML数据描述一种可能的格式,或者指定一个外部的处理程序。
我们使用<!NOTATION…>语句定义,有两种语法格式,分别定义普通记号和公用记号:
记号类型 |
定义语法 |
普通记号 |
<!NOTATION 符号名 SYSTEM "记号值"> |
公用记号 |
<!NOTATION 符号名 PUBLIC "公用符号标识名" "记号值"> |
记号值通常有两种形式:
① MIME:通用MIME类型的文件总是由相应的程序负责处理。
<!NOTATION gif SYSTEM "image/gif">
② 外部程序所在路径:直接指定某个外部程序负责处理XML文档中的外部数据。
<!NOTATION gif SYSTEM "iexplore.exe">
五、Schema约束
同DTD一样,XML Schema(XML Schema Definition,XSD,XML Schema定义)也是一种用于定义和描述XML文档结构与内容的模式语言,它的出现克服了DTD 的局限性。接下来,通过XML Schema与DTD的比较,将XML Schema所具有的一些显著优点进行列举,具体如下:
(1)DTD采用的是非XML语法格式,缺乏对文档结构、元素、数据类型等全面的描述。而XML Schema采用的是XML语法格式,而且它本身也是一种XML文档,因此,XML Schema语法格式比DTD更好理解。
(2)XML有非常高的合法性要求,虽然DTD和XML Schema都用于对XML文档进行描述,都被用作验证XML合法性的基础。但是,DTD本身合法性的验证必须采用另外一套机制,而XML Schema则采用与XML文档相同的合法性验证机制。
(3)XML Schema对名称空间支持得非常好,而DTD几乎不支持名称空间。
(4)DTD支持的数据类型非常有限。例如,DTD可以指定元素中必须包含字符文本(PCDATA),但无法指定元素中必须包含非负整数,而XML Schema比DTD支持更多的数据类型,包括用户自定义的数据类型。
(5)DTD定义约束的能力非常有限,无法对XML实例文档作出更细致的语义限制,例如,无法很好地指定一个元素中的某个子元素必须出现7-12次;而XML Schema定义约束的能力非常强大,可以对XML实例文档作出细致的语义限制。
通过上面的比较可以发现,XML Schema的功能比DTD强大很多,但相应的语法也比DTD复杂很多。Schema是基于XML编写的,XML Schema约束文件本身就是一个XML文档(文件后缀名为.xsd),文件内的代码要符合XML语法规范。
【复习】DTD文件语法:
<!ELEMENT 元素名称 元素内容> <!ATTLIST 元素名称 属性名称1 属性类型 设置说明 属性名称2 属性类型 设置说明 …… >
那么,我们使用Eclispe向导新建一个“XML Schema File”,扩展名是.xsd,名为NewXMLSchema.xsd,代码如下:
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/NewXMLSchema" xmlns:tns="http://www.example.org/NewXMLSchema" elementFormDefault="qualified"> </schema>
上面的代码中:
第1行是文档声明,与xml文档声明一样。
第2行中以<schema>为根元素,表示模式定义的开始。
- 属性xmlns="http://www.w3.org/2001/XMLSchema"表示使用了标准的命名空间(ns,namespace),与此命名空间相关联的 URI 是 Schema 的语言定义(Schema Language Definition),其标准值是 http://www.w3.org/2001/XMLSchema。表示当前文件是一个XML Schema约束文件;
- 属性targetNamespace="http://www.example.org/NewXMLSchema"表示的是如果要使用这个Schema约束文件,可以直接通过这个地址引入约束文件;
- 属性xmlns:tns="http://www.example.org/NewXMLSchema"表示这个Schema约束文件他本身的名称空间;
- 属性elementFormDefault="qualified"表示是质量良好的。
一个XML只能引入一个DTD约束文档,但是一个XML中可以有多个Schema约束文档,多个Schema使用名称空间区分(类似于Java 的类是按照Java包名来区分的是一个道理)。
1、名称空间
一个XML文档可以引入多个Schema约束文档,但是,由于约束文档中的元素或属性都是自定义的,因此,在XML文档中,极有可能出现代表不同含义的同名元素或属性,导致名称发生冲突。为此,在XML文档中,提供了名称空间,它可以唯一标识一个元素或者属性。这就好比咱们系有两个同名的同学,如果老师要找那个同学,就得给他们的名字前面加个前缀,XXX班的某某某。这个“XXX班”就相当于一个名称空间。
在使用名称空间时,首先必须声明名称空间。名称空间的声明就是在XML实例文档中为某个模式文档的名称空间指定一个临时的简写名称(起个别名),它通过一系列的保留属性来声明,这种属性的名字必须是以“xmlns”或者以“xmlns:”作为开始。它与其它任何XML属性一样,都可以通过直接或者使用默认的方式给出。
名称空间声明的语法格式如下所示:
<元素名称 xmlns:prefixname="URI">
在上述语法格式中,元素名称指的是在哪一个元素上声明名称空间,在这个元素上声明的名称空间适用于声明它的元素和属性,以及该元素中嵌套的所有元素及其属性。xmlns:prefixname指的是该元素的属性名,它所对应的值是一个URI引用,用来标识该名称空间的名称。
我们来修改book.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <xs:书架 xmlns:xs="http://www.example.org/book/schema" xmlns="http://www.w3.org/2001/XMLSchema-instance"> <xs:书> <xs:书名>JavaWeb程序开发入门</xs:书名> <xs:作者>传智播客</xs:作者> <xs:售价>60</xs:售价> </xs:书> </xs:书架>
名称空间的使用就是将一个前缀(xs)绑定到代表某个名称空间的URI(http://www.example.org/book/schema)上。
然后将前缀添加到元素名称前面来说明该元素属于哪个Schema文档。
如果一个XML文档有很多元素,而且这些元素都在同一个名称空间,这时,给每个元素名称都添加一个前缀将是一件非常烦琐的事情。
这时,可以使用默认的名称空间,默认名称空间声明时不需要加“别名”,使用这些元素时,也不用加前缀。
<?xml version="1.0" encoding="UTF-8"?> <xs:书架 xmlns:xs=http://www.example.org/book/schema> <xs:书> <xs:书名>JavaWeb程序开发入门</xs:书名> <xs:作者>传智播客</xs:作者> <xs:售价>60</xs:售价> </xs:书> </xs:书架>
2、引入Schema文档
如果想通过XML Schema文件对某个XML文档进行约束,必须将XML文档与Schema文件进行关联。在XML文档中引入Schema文件有两种方式:
(1)使用名称空间引入Schema
上面的书架book.xml文件对应的XML Schema约束文件book.xsd:
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/book" xmlns:tns="http://www.example.org/book" elementFormDefault="qualified"> <element name="书架"> <complexType> <sequence> <element name="书"> <complexType> <sequence> <element name="书名" type="string" /> <element name="作者" type="string" /> <element name="售价" type="string" /> </sequence> </complexType> </element> </sequence> </complexType> </element> </schema>
那么,我们需要在XML文档中的根节点中使用schemaLocation属性来指定Schema文件。schemaLocation属性有两个值:
第一个值是需要使用的名称空间;
第二个值是供命名空间使用的XML Schema文件的路径;
两者之间用空格分隔。
我们来创建book.xml文件时,可以使用Eclipse的新建XML文件向导。
修改生成的book.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <书架 xmlns="http://www.example.org/book" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/book book.xsd "> <书> <书名>JavaWeb程序开发入门</书名> <作者>传智播客</作者> <售价>60</售价> </书> </书架>
如果要使用名称空间引入多个XML Schema文档。修改book.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <书架 xmlns="http://www.example.org/book" xmlns:demo="http://www.example.org/demo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/book book.xsd http://www.example.org/demo demo.xsd "> <书> <书名>JavaWeb程序开发入门</书名> <作者>传智播客</作者> <售价 demo:类型="元">60</售价> </书> </书架>
(2)不使用名称空间引入Schema
如果book.xsd与引用它的XML文件位于同一个目录中,我们可以不使用名称空间来引入Schema,book.xsd中不需要定义targetNamespace(目标名称空间)和xmlns:tns(所在名称空间)了,book.xsd代码:
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <element name="书架"> <complexType> <sequence> <element name="书"> <complexType> <sequence> <element name="书名" type="string" /> <element name="作者" type="string" /> <element name="售价" type="string" /> </sequence> </complexType> </element> </sequence> </complexType> </element> </schema>
如果这时再用Eclipse向导来创建基于book.xsd的XML文件,就是如下图所示了。
修改book.xml文件,在book.xml中也不需要指定默认名称空间了。
<?xml version="1.0" encoding="UTF-8"?> <书架 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="book.xsd"> <书> <书名>JavaWeb程序开发入门</书名> <作者>传智播客</作者> <售价>60</售价> </书> </书架>
noNamespaceSchemaLocation是一个在标准名称空间http://www.w3.org/2001/XMLSchema-instance里面定义好的属性,用于指定book.xsd文档位置。
3、Schema语法
(1)元素定义
Schema和DTD一样,都可以定义XML文档中的元素。在Schema文档中,元素定义的语法格式如下所示:
<element name="xxx" type="yyy" />
在上面的语法格式中,element用于声明一个元素,这里使用默认名称空间,所以省去了前缀xs:,xxx指的是元素的名称,yyy指元素的数据类型。在XML Schema中有很多内建的数据类型,其中最常用的有以下几种(数据类型也是,省去了前缀xs:):
- string:表示字符串类型
- decimal:表示小数类型
- integer:表示整数类型
- boolean:表示布尔类型
- date:表示日期类型
- time:表示时间类型
- positiveInteger:表示正整数。
接下来看一个XML的示例代码:
<lastname>Smith</lastname> <age>28</age> <birthday>1990-03-12</birthday>
上面的XML示例代码中,定义了三个元素,这三个元素对应的Schema定义如下所示:
<element name="lastname" type="string" /> <element name="age" type="integer" /> <element name="birthday" type="date" />
(2)属性定义
在Schema文档中,属性定义的语法格式如下所示:
<attribute name="xxx" type="yyy" />
在上面的语法格式中,xxx指的是属性名称,yyy指的是属性的数据类型。其中,属性的常用数据类型与元素相同,都使用的是XML Schema中内建的数据类型。
了解了属性的定义方式,接下来,看一个XML的简单例子,具体示例如下所示:
<lastname lang="EN">Smith</lastname>
在上面的元素定义中,属性名称是lang,属性值的类型是字符串类型,因此,对应的Schema中的属性定义如下:
<attribute name="lang" type="string" />
(3)简单类型<simpleType>
只包含字符数据的元素都是简单类型,使用<simpleType>元素来定义。
如果想对现有元素内容的类型进行进一步限制,则需要使用<restriction>元素(restriction,限制,限定)
① minInclusive和maxInclusive元素对值的限定(最值约束)
如果要定义一个员工的年龄时,年龄要求在18-58周岁之间,我们需要对年龄age这个元素进行限定,具体Schema代码如下:
<element name="age"> <simpleType> <restriction base="integer"> <minInclusive value="18" /> <maxInclusive value="58" /> </restriction> </simpleType> </element>
上面的代码表示,元素age是integer类型,最小值包括18,最大值包括58。
正确XML: <age>20</age>
另外,类似的还有两个:
minExclusive:定义数值的下限。所允许的值必须大于此值(不包括此值)。
maxExclusive:定义数值的上限。所允许的值必须小于此值(不包括此值)。
② enumeration元素对于一组值的限定(枚举约束)
如果希望XML中的某个元素的内容限制在一组值中的一个,我们可以使用枚举约束,例如,要限定一个元素名为car的元素,它可接收的值只有Benz、BMW、Audi,具体Schema代码如下:
<element name="car"> <simpleType> <restriction base="string"> <enumeration value="Benz" /> <enumeration value="BMW" /> <enumeration value="Audi" /> </restriction> </simpleType> </element>
正确XML:<car>Benz</car>
③ pattern元素对于一系列值的限定(模式约束)
常用正则表达式 [abc] a、b 或 c(简单类) [^abc] 任何字符,除了 a、b 或 c(否定) [a-zA-Z] a到 z 或 A 到 Z,两头的字母包括在内(范围) X? X,一次或零次 X* X,零次或多次 X+ X,一次或多次 X{n} X,恰好 n 次 X{n,} X,至少 n 次 X{n,m} X,至少 n 次,但是不超过 m 次 |
如果希望把XML中的某个元素的内容限制在一系列可使用的数字和字母中,可以使用模式约束。例如,要定义一个带有限定的元素letter,要求可接受的值只能是a-z其中的一个,具体Schema代码如下:
<element name="letter"> <simpleType> <restriction base="string"> <pattern value="[a-z]" /> </restriction> </simpleType> </element>
正确XML: <letter>a</letter>
④ whiteSpace元素对空白字符的限定(空格约束)
在XML文档中,空白字符比较特殊,如果需要对空白字符进行处理,可以使用whiteSpace进行约束。whiteSpace元素有三个属性值:
- preserve:不对元素中的任何空白字符进行处理(保持原状)
- replace:移除所有空白字符
- collapse:将所有空白字符缩减为一个单一字符
例如,对于地址address元素内容中的空白字符不做任何处理,具体Schema代码如下:
<element name="address"> <simpleType> <restriction base="string"> <whiteSpace value="preserve" /> </restriction> </simpleType> </element>
正确XML: <address>abc def ghi</address>
(4)复杂类型<complexType>
除简单类型之外的其他类型都是复杂类型,在定义复杂类型时,需要使用<complexType>元素来定义。复杂类型的元素可以包含子元素和属性,这样的元素称为复合元素。在定义复合元素时,如果元素的开始标记和结束标记之间只包含字符数据内容,那么这样的内容是简易内容,需要使用<simpleContent>元素来定义。反之,元素的内容都是复杂内容,需要使用<complexContent>元素来定义。复合元素有4种基本类型:
① 空元素
这里的空元素指的是不包含内容,只包含属性的元素,具体示例如下:
<product prodid="1345" />
在上面的元素定义中,没有定义元素product的内容,这时,空元素在XML Schema文档中对应的定义方式如下所示:
<element nane="product"> <complexType> <attribute name="prodid" type="positiveInteger" /> </complexType> </element>
属性类型positiveInteger表示正整数。
② 包含其他元素的元素
XML文档中包含其他元素的元素,例如下面的示例代码:
<person> <firstname>John</firstname> <lastname>Smith</lastname> </person>
在上面的示例代码中,元素person 嵌套了两个元素,分别是firstname和lastname。这时,在Schema文档中对应的定义方式如下所示:
<element name="person"> <complexType> <sequence> <element name="firstname" type="string" /> <element name="lastname" type="string" /> </sequence> </complexType> </element>
③ 仅包含文本的元素----简易内容
对于仅包含文本的复合元素,需要使用simpleContent元素来添加内容。在使用简易内容时,必须在simpleContent元素内定义扩展或限定,这时,需要使用extension 或restriction元素来扩展或限制元素的基本简易类型。请看-个XML的简易例子,其中,"shoesize"仅包含文本,具体示例如下:
<shoesize country="france">35</shoesize>
在上面的例子中元素shoesize 包含属性以及元素内容,针对这种仅包含文本的元素,需要使用<extension>来对元素的类型进行扩展,在Schema文档中对应的定义方式如下所示:
<element name="shoesize"> <complexType> <simpleContent> <extension base="integer"> <attribute name="country" type="string" /> </extension> </simpleContent> </complexType> </element>
④ 包含元素和文本的元素
在XML文档中,某些元素经常需要包含文本以及其他元素,例如,下面的这段XML文档:
<letter> Dear Mr.<name>John smith</name>. Your order<orderid>1032</orderid> will be shipped on<shipdate>2001-07-13</shipdate> </letter>
上面的这段XML文档,在Schema文档中对应的定义方式如下所示:
<element name="letter"> <complexType mixed="true"> <sequence> <element name="name" type="string" /> <element name="orderid" type="positiveInteger" /> <element name="shipdate" type="date" /> </sequence> </complexType> </element>
需要注意的是,为了使字符数据可以出现在letter元素的子元素之间,使用了mixed属性,该属性是用来规定是否允许字符数据出现在复杂类型的子元素之间,默认情况下mixed的值是false。
最后,整个person.xsd代码如下:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.org/person/schema" targetNamespace="http://www.example.org/person/schema" elementFormDefault="qualified"> <xs:element name="person"> <xs:complexType> <xs:sequence> <xs:element name="firstname" type="xs:string" /> <xs:element name="lastname" type="xs:string" /> <xs:element name="age"> <xs:simpleType> <xs:restriction base="xs:integer"> <xs:minInclusive value="18" /> <xs:maxInclusive value="58" /> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name="birthday" type="xs:date" /> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
整个person.xml代码如下:
<?xml version="1.0" encoding="UTF-8"?> <person xmlns="http://www.example.org/person/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/person/schema person.xsd"> <firstname>John</firstname> <lastname>Smith</lastname> <age>18</age> <birthday>1990-03-12</birthday> </person>
【DTD约束形式】
<!ELEMENT person (firstname, lastname, age, birthday)> <!ELEMENT firstname (#PCDATA)> <!ELEMENT lastname (#PCDATA)> <!ELEMENT age (#PCDATA)> <!ELEMENT birthday (#PCDATA)>