Java Web程序设计任务教程
上QQ阅读APP看书,第一时间看更新

2.1 XML基础

在实际开发中,由于不同语言项目之间数据传递的格式有可能不兼容,导致这些项目在数据传输时变得很困难。为解决此问题,W3C 组织推出了一种新的数据交换标准——XML,它是一种通用的数据交换格式,可以使数据在各种应用程序之间轻松地实现数据的交换。接下来,本节将对XML进行详细的讲解。

2.1.1 XML概述

1.什么是XML

在现实生活中,很多事物之间都存在着一定的关联关系,例如中国有很多省份,每个省份下又有很多城市,这些省市之间的关联关系可以通过一张树状结构图来描述,具体如图2-1所示。

图2-1 城市关系图

图2-1直观地描述了中国与所辖省、市之间的层次关系。但是对于程序而言,解析图片内容非常困难,这时,采用XML文件保存这种具有树状结构的数据是最好的选择。

XML是Extensible Markup Language的缩写,它是一种类似于HTML的标记语言,称为可扩展标记语言。所谓可扩展,指的是用户可以按照XML规则自定义标记。

下面通过一个XML文档来描述图2-1所示的关系,如文件2-1所示。

文件2-1 city.xml

1 <?xml version="1.0" encoding="UTF-8"?>

2 <中国>

3   <河北>

4     <城市>张家口</城市>

5     <城市>石家庄</城市>

6   </河北>

7   <山西>

8     <城市>太原</城市>

9     <城市>大同</城市>

10   </山西>

11 </中国>

在文件2-1中,第1行代码是XML的文档声明,下面的<中国>、<河北>、<城市>都是用户自己创建的标记,它们都可称为元素,这些元素必须成对出现,即包括开始标记和结束标记。例如,在<中国>元素中的开始标记为<中国>,结束标记为</中国>。<中国>被视为整个XML文档的根元素,在它下面有两个子元素,分别是<河北>和<山西>,在这两个子元素中又分别包含两个<城市>元素。在XML文档中,通过元素的嵌套关系可以很准确地描述具有树状层次结构的复杂信息。因此,越来越多的应用程序都采用XML格式来存放相关的配置信息,以便于读取和修改。

2.XML与HTML的比较

XML和HTML都是标记文本,它们在结构上大致相同,都是以标记的形式来描述信息。但实际上它们有着本质的区别,为了让读者不产生混淆,接下来对HTML和XML进行比较,具体如下。

(1)HTML被设计出是用来显示数据的,XML被设计出是为了传输和存储数据的。

(2)HTML是不区分大小写的,而XML是严格区分大小写的。

(3)HTML可以有多个根元素,而格式良好的XML有且只能有一个根元素。

(4)HTML中,空格是自动过滤的,而XML中空格则不会自动删除。

(5)HTML中的标记是预定义的,而XML中的标记可以根据需要自己定义,并且可扩展。

总之,XML不是HTML的升级,也不是HTML的替代产品,虽然两者有些相似,但它们的应用领域和范围完全不同。在大多数Web应用程序中,XML用于传输数据,而HTML用于格式化并显示数据。HTML规范的最新版本是HTML5,但其常见版本是HTML 4.01。此外,HTML还有一个常见版本——XHTML(EXtensible HyperText Markup Language,即可扩展超文本标签语言),它是XML应用被重新定义的HTML,是更严格更纯净的HTML版本。

2.1.2 XML语法

1.文档声明

在一个完整的XML文档中,必须包含一个XML文档的声明,并且该声明必须位于文档的第1行。这个声明表示该文档是一个XML文档,以及遵循哪个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,表示文档依赖于外部文档。

2.元素定义

在XML文档中,主体内容都是由元素(Element)组成的。元素一般是由开始标记、属性、元素内容和结束标记构成,具体示例如下。

<城市>北京</城市>

上面的示例中,“<城市>”和“</城市>”就是XML文档中的标记,标记的名称也就是元素的名称。在一个元素中可以嵌套若干子元素。如果一个元素没有嵌套在其他元素内,则这个元素称为根元素。根元素是XML文档定义的第一个元素。如果一个元素中没有嵌套子元素,也没有包含文本内容,则这样的元素称为空元素,空元素可以不使用结束标记,但必须在起始标记的“>”前增加一个正斜杠“/”来说明该元素是个空元素,例如:<img></img>可以简写成<img/>。

3.属性定义

在XML文档中,可以为元素定义属性。属性是对元素的进一步描述和说明。在一个元素中,可以有多个属性,并且每个属性都有自己的名称和取值,具体示例如下:

<售价 单位="元">68</售价>

在上面的示例中,<售价>元素中定义了一个属性“单位”。需要注意的是,在XML文档中,属性的命名规范同元素相同,属性值必须要用双引号("")或者单引号('')引起来,否则被视为错误。

4.注释

如果想在XML文档中插入一些附加信息,比如作者姓名、地址或电话等信息,或者想暂时屏蔽某些XML语句,这时,可以通过注释的方式来实现,被注释的内容会被程序忽略而不被解析和处理。XML注释和HTML注释写法基本一致,具体语法格式如下所示。

<!--注释信息-->

2.1.3 DTD约束

1.什么是约束

在现实生活中,如果一篇文章的语法正确,但内容包含违法言论或逻辑错误,这样的文章是不允许发表的。同样,在书写XML文档时,其内容必须满足某些条件的限制,先来看一个例子,具体如下:

<?xml version="1.0" encoding="UTF-8"?>

<书架>

<书>

<书名>Java就业培训教程</书名>

<作者 姓名="张孝祥"/>

<售价 单位="元">38</售价>

<售价 单位="元">28</售价>

</书>

</书架>

在上面的示例中,尽管这个XML文档结构是正确的,用IE浏览器打开它也不会出现任何问题,但是,由于XML文档中的标记是可以随意定义的,同一本书出现了两种售价,如果仅根据标记名称区分哪个是原价,哪个是会员价,这是很难实现的。为此,在XML文档中,定义了一套规则来对文档中的内容进行约束,这套约束称为XML约束。

对XML文档进行约束时,同样需要遵守一定的语法规则,这种语法规则就形成了XML约束语言。目前,最常用的两种约束语言是DTD约束和Schema约束,接下来,将针对这两种约束进行详细的讲解。

2.DTD约束

DTD约束是早期出现的一种XML约束模式语言,根据它的语法创建的文件称为DTD文件。在一个DTD文件中,可以包含元素的定义、元素之间关系的定义、元素属性的定义以及实体和符号的定义。接下来通过一个案例来简单认识一下DTD约束,如文件2-2和文件2-3所示。

文件2-2 book.xml

1 <?xml version="1.0" encoding="UTF-8"?>

2  <书架>

3   <书>

4     <书名>Java就业培训教程</书名>

5     <作者>张孝祥</作者>

6     <售价>58.00元</售价>

7   </书>

8   <书>

9     <书名>EJB3.0入门经典</书名>

10    <作者>黎活明</作者>

11    <售价>39.00元</售价>

12   </书>

13 </书架>

文件2-3 book.dtd

1 <!ELEMENT 书架 (书+)>

2 <!ELEMENT 书 (书名,作者,售价)>

3 <!ELEMENT 书名 (#PCDATA)>

4 <!ELEMENT 作者 (#PCDATA)>

5 <!ELEMENT 售价 (#PCDATA)>

文件2-3所示的book.dtd是一个简单的DTD约束文档。在文件2-2中,book.xml中定义的每个元素都是按照book.dtd文档所规定的约束进行编写的。接下来针对文件2-3所示的约束文档进行详细的讲解,具体如下。

(1)在第1行中,使用<!ELEMENT …>语句定义了一个元素,其中“书架”是元素的名称,“(书+)”表示书架元素中有一个或者多个书元素,字符“+”用来表示它所修饰的成分必须出现一次或者多次。

(2)在第2行中,“书”是元素名称,“(书名,作者,售价)”表示元素书包含书名、作者、售价这3个子元素,并且这些子元素要按照顺序依次出现。

(3)在第3~5行中,“书名”“作者”和“售价”都是元素名称,“(#PCDATA)”表示元素中嵌套的内容是普通的文本字符串。

3.DTD的引入

对DTD文件有了大致了解后,如果想使用DTD文件约束XML文档,必须在XML文档中引入DTD文件。在XML文档中引入外部DTD文件有两种方式,具体如下:

1 <!DOCTYPE 根元素名称 SYSTEM "外部DTD文件的URI">

2 <!DOCTYPE 根元素名称 PUBLIC "DTD名称" "外部DTD文件的URI">

在上述两种引入DTD文件的方式中,第1种方式用来引用本地的DTD文件,第2种方式用来引用公共的DTD文件,其中“外部DTD文件的URI”指的是DTD文件的存放位置,对于第1种方式,它可以是相对于XML文档的相对路径,也可以是一个绝对路径;而对于第2种方式,它是Internet上的一个绝对URL地址。

接下来对文件2-2进行修改,在XML文档中引入本地的DTD文件book.dtd,如文件2-4所示。

文件2-4 book.xml

1 <?xml version="1.0" encoding="UTF-8"?>

2 <!DOCTYPE 书架 SYSTEM "book.dtd">

3 <书架>

4   <书>

5     <书名>Java就业培训教程</书名>

6     <作者>张孝祥</作者>

7     <售价>58.00元</售价>

8   </书>

9   <书>

10    <书名>EJB3.0入门经典</书名>

11    <作者>黎活明</作者>

12    <售价>39.00元</售价>

13   </书>

14 </书架>

在文件2-4中,由于引入的是本地的DTD文件,因此,使用的是SYSTEM属性的DOCTYPE声明语句。另外,在XML文档的声明语句中,standalone属性不能设置为“yes”。

如果希望引入一个公共的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标识名称进行复制就可以了。

DTD对XML文档的约束,除了外部引入方式实现外,还可以采用内嵌的方式。在XML中直接嵌入DTD定义语句的完整语法格式如下所示。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<!DOCTYPE 根元素名 [

DTD定义语句

……

]>

接下来对文件2-4进行修改,在book.xml文档中直接嵌入book.dtd文件,修改后的代码如文件2-5所示。

文件2-5 book.xml

1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>

2 <!DOCTYPE 书架 [

3   <!ELEMENT 书架 (书+)>

4   <!ELEMENT 书 (书名,作者,售价)>

5   <!ELEMENT 书名 (#PCDATA)>

6   <!ELEMENT 作者 (#PCDATA)>

7   <!ELEMENT 售价 (#PCDATA)>

8 ]>

9 <书架>

10   <书>

11    <书名>Java就业培训教程</书名>

12    <作者>张孝祥</作者>

13    <售价>58.00元</售价>

14   </书>

15   <书>

16    <书名>EJB3.0入门经典</书名>

17    <作者>黎活明</作者>

18    <售价>39.00元</售价>

19   </书>

20 </书架>

文件2-5实现了在XML文档内部直接嵌入DTD语句。需要注意的是,由于一个DTD文件可能会被多个XML文件引用,因此,为了避免在每个XML文档中都添加一段相同的DTD定义语句,通常都将其放在一个单独的DTD文档中定义,采用外部引用的方式对XML文档进行约束。这样,不仅便于管理和维护DTD定义,还可以使多个XML文档共享一个DTD文件。

4.DTD语法

在编写XML文档时,需要掌握XML语法。同理,在编写DTD文档时,也需要遵循一定的语法。DTD的结构一般由元素类型定义、属性定义、实体定义、记号(Notation)定义等构成,一个典型的文档类型定义会把将来要创建的XML文档的元素结构、属性类型、实体引用等预先进行定义。接下来,针对DTD结构中所涉及的语法进行详细讲解。

(1)元素定义

元素是XML文档的基本组成部分,在DTD定义中,每一条<!ELEMENT…>语句用于定义一个元素,其基本的语法格式如下所示。

<!ELEMENT 元素名称 元素内容>

在上面元素的定义格式中,包含了“元素名称”和“元素内容”。其中,“元素名称”是自定义的名称,它用于定义被约束XML文档中的元素;“元素内容”是对元素包含内容的声明,包括数据类型和符号两部分,它共有5种内容形式,具体如下。

1)#PCDATA:表示元素中嵌套的内容是普通文本字符串,其中,关键字PCDATA是Parsed Character Data的简写。例如,<!ELEMENT书名(#PCDATA)>表示书名所嵌套的内容是字符串类型。

2)子元素:说明元素包含的元素。通常用一对圆括号()将元素中要嵌套的一组子元素括起来,例如,<!ELEMENT 书(书名,作者,售价)>表示元素书中要嵌套书名、作者、售价等子元素。

3)混合内容:表示元素既可以包含字符数据,也可以包含子元素。混合内容必须被定义零个或多个,例如,<!ELEMENT 书(#PCDATA|书名)*>表示书中嵌套的子元素书名包含零个或多个,并且书名是字符串文本格式。

4)EMPTY:表示该元素既不包含字符数据,也不包含子元素,是一个空元素。如果在文档中元素本身已经表明了明确的含义,就可以在 DTD 中用关键字 EMPTY 表明空元素。例如,<!ELEMENT br EMPTY>,其中,br是一个没有内容的空元素。

5)ANY:表示该元素可以包含任何的字符数据和子元素。例如,<!ELEMENT联系人ANY>表示联系人可以包含任何形式的内容。但在实际开发中,应该尽量避免使用 ANY,因为除了根元素外,其他使用ANY的元素都将失去DTD对XML文档的约束效果。

需要注意的是,在定义元素时,元素内容中可以包含一些符号,不同的符号具有不同的作用。接下来,针对一些常见的符号进行讲解,具体如下。

1)问号[?]:表示该对象可以出现0次或1次。

2)星号[*]:表示该对象可以出现0次或多次。

3)加号[+]:表示该对象可以出现1次或多次。

4)竖线[|]:表示在列出的对象中选择1个。

5)逗号[,]:表示对象必须按照指定的顺序出现。

6)括号[()]:用于给元素进行分组。

(2)属性定义

在DTD文档中,定义元素的同时,还可以为元素定义属性。DTD属性定义的基本语法格式如下所示。

<!ATTLIST 元素名

属性名1 属性类型 设置说明

属性名2 属性类型 设置说明

……

>

在上面属性定义的语法格式中,“元素名”是属性所属元素的名字,“属性名”是属性的名称,“属性类型”则用来指定该属性是属于哪种类型,“设置说明”用来说明该属性是否必须出现。关于“属性类型”和“设置说明”的相关讲解,具体如下。

1)设置说明

定义元素的属性时,有4种设置说明可以选择,具体如表2-1所示。

表2-1 设置说明

2)属性类型

在DTD中定义元素的属性时,有10种属性类型可以选择,常见的几种属性类型介绍如下。

① CDATA

这是最常用的一种属性类型,表明属性类型是字符数据,与元素内容说明中的#PCDATA相同。当然,在属性设置值中出现的特殊字符,也需要使用其转义字符序列来表示,例如,用“&amp;”表示字符“&”,用“&lt;”表示字符“<”等。

② Enumerated(枚举类型)

在声明属性时,可以限制属性的取值只能从一个列表中选择,这类属性属于Enumerated(枚举类型)。需要注意的是,在DTD定义中并不会出现关键字Enumerated。接下来通过一个案例来学习如何定义Enumerated类型的属性,如文件2-6所示。

文件2-6 enum.xml

1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>

2 <!DOCTYPE 购物篮 [

3   <!ELEMENT 购物篮 ANY>

4   <!ELEMENT 肉 EMPTY>

5   <!ATTLIST 肉 品种 (鸡肉|牛肉|猪肉|鱼肉) "鸡肉">

6 ]>

7 <购物篮>

8   <肉 品种="鱼肉"/>

9   <肉 品种="牛肉"/>

10  <肉/>

11 </购物篮>

在文件2-6中,“品种”属性的类型是Enumerated,其值只能为 “鸡肉”“牛肉”“猪肉”和“鱼肉”,而不能使用其他值。“品种”属性的默认值是“鸡肉”,所以,即使<购物篮>元素中的第3个子元素没有显示定义“品种”这个属性,但它实际上也具有“品种”这个属性,且属性的取值为“鸡肉”。

③ ID

一个ID类型的属性用于唯一标识XML文档中的一个元素。其属性值必须遵守XML名称定义的规则。一个元素只能有一个 ID 类型的属性,而且 ID 类型的属性必须设置为#IMPLIED 或#REQUIRED。因为ID类型属性的每一个取值都是用来标识一个特定的元素,所以,为ID类型的属性提供默认值,特别是固定的默认值是毫无意义的。接下来通过一个案例来学习如何定义一个ID类型的属性,如文件2-7所示。

文件2-7 id.xml

1 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>

2 <!DOCTYPE 联系人列表[

3   <!ELEMENT 联系人列表 ANY>

4   <!ELEMENT 联系人 (姓名,EMAIL)>

5   <!ELEMENT 姓名 (#PCDATA)>

6   <!ELEMENT EMAIL (#PCDATA)>

7   <!ATTLIST 联系人 编号 ID #REQUIRED>

8 ]>

9 <联系人列表>

10  <联系人 编号="id1">

11     <姓名>张三</姓名>

12     <EMAIL>zhang@itcast.cn</EMAIL>

13  </联系人>

14  <联系人 编号="id2">

15     <姓名>李四</姓名>

16     <EMAIL>li@itcast.cn</EMAIL>

17  </联系人>

18 </联系人列表>

在文件2-7中,将元素为<联系人>的编号属性设置为#REQUIRED,说明每个联系人都有一个编号,同时,属性编号的类型为 ID,说明编号是唯一的。如此一来,通过编号就可以找到唯一对应的联系人了。

④ IDREF和IDREFS

文件2-7中,虽然张三和李四两个联系人的ID编号是唯一的,但是这两个ID类型的属性没有发挥作用,这时可以使用 IDREF 类型,使这两个联系人之间建立一种一对一的关系。接下来通过一个案例来学习IDREF类型的使用,如文件2-8所示。

文件2-8 Idref.xml

1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>

2 <!DOCTYPE 联系人列表[

3   <!ELEMENT 联系人列表 ANY>

4   <!ELEMENT 联系人 (姓名,EMAIL)>

5   <!ELEMENT 姓名 (#PCDATA)>

6   <!ELEMENT EMAIL (#PCDATA)>

7   <!ATTLIST 联系人

8        编号 ID #REQUIRED

9        上司 IDREF #IMPLIED>

10 ]>

11 <联系人列表>

12  <联系人编号="id1">

13    <姓名>张三</姓名>

14     <EMAIL>zhang@itcast.org</EMAIL>

15  </联系人>

16  <联系人编号="id2"上司="id1">

17    <姓名>李四</姓名>

18     <EMAIL>li@itcast.org</EMAIL>

19  </联系人>

20 </联系人列表>

在文件2-8中,为元素<联系人列表>的子元素<联系人>增加了一个名称为上司的属性,并且将该属性的类型设置为IDREF,IDREF类型属性的值必须为一个已经存在的ID类型的属性值。在第2个<联系人>元素中,将“上司”属性设置为第1个联系人的编号属性值,如此一来,就形成了两个联系人元素之间的对应关系,即李四的上司为张三。

IDREF类型可以使两个元素之间建立一对一的关系,但是,如果两个元素之间的关系是一对多,例如,一个学生去图书馆可以借多本书,这时,需要使用IDREFS类型来指定某个人借阅了哪些书。需要注意的是,IDREFS类型的属性可以引用多个ID类型的属性值,这些ID的属性值需要用空格分隔。接下来通过一个案例来学习IDREFS的使用,如文件2-9所示。

文件2-9 Library.xml

1 <?xml version="1.0" encoding="UTF-8"?>

2 <!DOCTYPE library[

3   <!ELEMENT libarary (books,records)>

4   <!ELEMENT books (book+)>

5   <!ELEMENT book (title)>

6   <!ELEMENT title (#PCDATA)>

7   <!ELEMENT records (item+)>

8   <!ELEMENT item (data,person)>

9   <!ELEMENT data (#PCDATA)>

10  <!ELEMENT person EMPTY>

11  <!ATTLIST book bookid ID #REQUIRED>

12  <!ATTLIST person name CDATA #REQUIRED>

13  <!ATTLIST person borrowed IDREFS #REQUIRED>

14 ]>

15 <library>

16  <books>

17   <book bookid="b0101">

18     <title>Java就业培训教材</title>

19   </book>

20   <book bookid="b0102">

21     <title>Java Web开发内幕 </title>

22   </book>

23   <book bookid="b0103">

24     <title>Java开发宝典</title>

25   </book>

26  </books>

27  <records>

28   <item>

29     <data>2013-03-13</data>

30     <person name="张三" borrowed="b0101 b0103"/>

31   </item>

32   <item>

33     <data>2013-05-23</data>

34     <person name="李四" borrowed="b0101 b0102 b0103"/>

35   </item>

36  </records>

37 </library>

文件2-9中,将元素<book>中属性名为bookid的属性设置为ID类型,元素<person>中名为borrowed的属性设置为IDREFS类型。从Library.xml文档中可以看出,张三借阅了《Java就业培训教材》和《Java开发宝典》这两本书,而李四则借阅了《Java就业培训教材》《Java Web开发内幕》和《Java开发宝典》这3本书。

除了上面内容中讲述的几种属性类型外,DTD 约束中还有 NMTOKEN、NMTOKENS、NOTATION、ENTITY和ENTITYS几种属性类型,由于篇幅有限,此处就不一一列举。

2.1.4 Schema约束

1.什么是Schema约束

同DTD一样,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),但无法指定元素中必须包含非负整数(nonNegativeInteger),而XML Schema比XML DTD支持更多的数据类型,包括用户自定义的数据类型。

(5)DTD定义约束的能力非常有限,无法对XML实例文档作出更细致的语义限制,例如,无法很好地指定一个元素中的某个子元素必须出现7~12次;而XML Schema定义约束的能力非常强大,可以对XML实例文档作出细致的语义限制。

通过上面的比较可以发现,XML Schema的功能比DTD强大很多,但相应的语法也比DTD复杂很多,接下来看一个简单的Schema文档,如文件2-10所示。

文件2-10 Simple.xsd

1 <?xml version="1.0"?>

2 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

3  <xs:element name="root" type="xs:string"/>

4 </xs:schema>

在文件2-10中,第1行是文档声明,第2行中以xs:schema作为根元素,表示模式定义的开始。由于根元素xs:schema的属性都在http://www.w3.org/2001/XMLSchema名称空间中,因此,在根元素上必须声明该名称空间。

2.名称空间

一个XML文档可以引入多个约束文档,但是,由于约束文档中的元素或属性都是自定义的,因此,在XML文档中,极有可能出现代表不同含义的同名元素或属性,导致名称发生冲突。为此,在XML文档中,提供了名称空间,它可以唯一标识一个元素或者属性。这就好比打车去小营,由于北京有两个地方叫小营,为了避免司机走错,我们就会说“去亚运村的小营”或者“去清河的小营”。这时的亚运村或者清河就相当于一个名称空间。

在使用名称空间时,首先必须声明名称空间。名称空间的声明就是在XML实例文档中为某个模式文档的名称空间指定一个临时的简写名称,它通过一系列的保留属性来声明,这种属性的名字必须是以“xmlns”或者以“xmlns:”作为前缀。它与其他任何XML属性一样,都可以通过直接或者使用默认的方式给出。名称空间声明的语法格式如下所示。

<元素名 xmlns:prefixname="URI">

在上述语法格式中,元素名指的是在哪一个元素上声明名称空间,在这个元素上声明的名称空间适用于声明它的元素和属性,以及该元素中嵌套的所有元素及其属性。xmlns:prefixname指的是该元素的属性名,它所对应的值是一个URI引用,用来标识该名称空间的名称。需要注意的是,如果有两个URI并且其组成的字符完全相同,就可以认为它们标识的是同一个名称空间。

了解了名称空间的声明方式,接下来通过一个案例来学习,如文件2-11所示。

文件2-11 book.xml

1 <?xml version="1.0" encoding="UTF-8"?>

2 <it315:书架 xmlns:it315="http://www.it315.org/xmlbook/schema">

3   <it315:书>

4     <it315:书名>JavaScript网页开发</it315:书名>

5     <it315:作者>张孝祥</it315:作者>

6     <it315:售价>28.00元</it315:售价>

7   </it315:书>

8 </it315:书架>

在文件2-11中,it315被作为多个元素名称的前缀部分,必须通过名称空间声明将它关联到唯一标识某个名称空间的 URI 上,xmlns:it315="http://www.it315.org/xmlbook/schema"语句就是将前缀名it315关联到名称空间"http://www.it315.org/xmlbook/schema"上。由此可见,名称空间的应用就是将一个前缀(如 it315)绑定到代表某个名称空间的 URI(如 http://www. it315.org/xmlbook/schema)上,然后将前缀添加到元素名称的前面(例如,it315:书)来说明该元素属于哪个模式文档。

需要注意的是,在声明名称空间时,有两个前缀是不允许使用的,它们是xml和xmlns。xml前缀被定义为与名称空间名字http://www.w3.org/XML/1998/namespace绑定,只能用于XML1.0规范中定义的xml:space和xml:lang属性。前缀xmlns仅用于声明名称空间的绑定,它被定义为与名称空间名字http://www.w3.org/2000/xmlns绑定。

3.引入Schema文档

若想通过XML Schema文件对某个XML文档进行约束,必须将XML文档与Schema文件进行关联。在XML文档中引入Schema文件有两种方式,具体如下。

(1)使用名称空间引入XML Schema文档

在使用名称空间引入XML Schema文档时,需要通过属性xsi:schemaLocation来声明名称空间的文档,xsi:schemaLocation 属性是在标准名称空间“http://www.w3.org/2001/XMLSchema-instance”中定义的,在该属性中,包含了两个URI,这两个URI之间用空白符分隔。其中,第1个URI是名称空间的名称,第2个URI是文档的位置,接下来,通过一个案例来演示如何使用名称空间引入XML Schema文档,如文件2-12所示。

文件2-12 book.xml

1 <?xml version="1.0" encoding="UTF-8"?>

2 <书架 xmlns="http://www.it315.org/xmlbook/schema"

3    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

4    xsi:schemaLocation="http://www.it315.org/xmlbook/schema

5              http://www.it315.org/xmlbook.xsd">

6   <书>

7     <书名>JavaScript网页开发</书名>

8     <作者>张孝祥</作者>

9     <售价>28.00元</售价>

10   </书>

11 </书架>

在文件2-12中,schemaLocation属性用于指定名称空间所对应的XML Schema文档的位置,由于 schemaLocation 属性是在另外一个公认的标准名称空间中定义的,因此,在使用schemaLocation属性时,必须要声明该属性所属的命名空间。

需要注意的是,一个 XML 实例文档可能引用多个名称空间,这时,可以在schemaLocation属性值中包含多对名称空间与它们所对应的XML Schema文档的存储位置,每一对名称空间的设置信息之间采用空格分隔。接下来通过一个案例来演示在一个XML文档中引入多个名称空间名称的情况,如文件2-13所示。

文件2-13 Xmlbook.xml

1 <?xml version="1.0" encoding="UTF-8"?>

2 <书架 xmlns="http://www.it315.org/xmlbook/schema"

3    xmlns:demo="http://www.it315.org/demo/schema"

4    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

5    xsi:schemaLocation="http://www.it315.org/xmlbook/schema

6              http://www.it315.org/xmlbook.xsd

7              http://www.it315.org/demo/schema

8              http://www.it315.org/demo.xsd">

9   <书>

10    <书名>JavaScript网页开发</书名>

11    <作者>张孝祥</作者>

12    <售价 demo:币种="人民币">28.00元</售价>

13   </书>

14 </书架>

(2)不使用名称空间引入XML Schema文档

在XML文档中引入XML Schema文档,不仅可以通过xsi:schemaLocation属性引入名称空间的文档,还可以通过 xsi:noNamespac3eSchemaLocation 属性直接指定,noNamespaceSchemaLocation属性也是在标准名称空间“http://www.w3.org/2001/XMLSchema-instance”中定义的,它用于定义指定文档的位置。接下来,通过一个案例来演示noNamespaceSchemaLocation属性在XML文档中的使用,如文件2-14所示。

文件2-14 Xmlbook.xml

1 <?xml version="1.0" encoding="UTF-8"?>

2 <书架 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

3   xsi:noNamespaceSchemaLocation="xmlbook.xsd">

4   <书>

5     <书名>JavaScript网页开发</书名>

6     <作者>张孝祥</作者>

7     <售价>28.00元</售价>

8   </书>

9 </书架>

在文件2-14中,文档xmlbook.xsd与引用它的实例文档位于同一目录中。

4.Schema语法

任何语言都有一定的语法,Schema也不例外。了解了Schema的基本用法,接下来,本节将针对Schema语法进行详细的讲解。

(1)元素定义

Schema和DTD一样,都可以定义XML文档中的元素。在Schema文档中,元素定义的语法格式如下所示。

<xs:element name="xxx" type="yyy"/>

在上面的语法格式中,element用于声明一个元素,xxx指的是元素的名称,yyy 指元素的数据类型。在XML Schema中有很多内建的数据类型,其中最常用的有以下几种。

• xs:string:表示字符串类型

• xs:decimal:表示小数类型

• xs:integer:表示整数类型

• xs:boolean:表示布尔类型

• xs:date:表示日期类型

• xs:time:表示时间类型

了解了元素的定义方式,接下来看一个XML的示例代码,具体示例如下。

<lastname>Smith</lastname>

<age>28</age>

<dateborn>1980-03-27</dateborn>

在上面的XML示例代码中,定义了3个元素,这3个元素对应的Schema定义如下所示。

<xs:element name="lastname" type="xs:string"/>

<xs:element name="age" type="xs:integer"/>

<xs:element name="dateborn" type="xs:date"/>

(2)属性的定义

在Schema文档中,属性定义的语法格式如下所示。

<xs:attribute name="xxx" type="yyy"/>

在上面的语法格式中,xxx指的是属性名称,yyy 指的是属性的数据类型。其中,属性的常用数据类型与元素相同,都使用的是XML Schema中内建的数据类型。

了解了属性的定义方式,接下来,看一个XML的简单例子,具体示例如下所示。

<lastname lang="EN">Smith</lastname>

在上面的这段简易XML例子中,属性的名称是lang,属性值的类型是字符串类型,因此,对应的Schema定义方式如下所示。

<xs:attribute name="lang" type="xs:string"/>

(3)简单类型

在 XML Schema 文档中,只包含字符数据的元素都是简单类型的。简单类型使用xs:simpleType元素来定义。如果想对现有元素内容的类型进行限制,则需要使用xs:restriction元素。接下来,通过以下几种情况详细介绍如何对简单类型元素的内容进行限定,具体如下。

1)xs:minInclusive和xs:maxInclusive元素对值的限定

例如,当我们定义一个雇员的年龄时,雇员的年龄要求是18~58周岁之间,这时,需要对年龄“age”这个元素进行限定,具体示例代码如下所示。

<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>

在上面的示例代码中,元素age的属性是integer,通过xs:minInclusive和xs:maxInclusive元素限制了年龄值的范围。

2)xs:enumeration元素对一组值的限定

如果希望将 XML 元素的内容限制为一组可接受的值,可以使用枚举约束(Enumeration Constraint),例如,要限定一个元素名为Car的元素,可接受的值只有Audi、Golf、BMW,具体示例如下。

<xs:element name="car">

<xs:simpleType>

<xs:restriction base="xs:string">

<xs:enumeration value="Audi"/>

<xs:enumeration value="Golf"/>

<xs:enumeration value="BMW"/>

</xs:restriction>

</xs:simpleType>

</xs:element>

3)xs:pattern元素对一系列值的限定

如果希望把 XML 元素的内容限制定义为一系列可使用的数字或字母,可以使用模式约束(Pattern Constraint)。例如,要定义一个带有限定的元素“letter”,要求可接受的值只能是字母a~z其中一个,具体示例如下。

<xs:element name="letter">

<xs:simpleType>

<xs:restriction base="xs:string">

<xs:pattern value="[a-z]"/>

</xs:restriction>

</xs:simpleType>

</xs:element>

4)xs:restriction元素对空白字符的限定

在XML文档中,空白字符比较特殊,如果需要对空白字符(Whitespace Characters)进行处理,可以使用whiteSpace元素。whiteSpace元素有3个属性值可以设定,分别是preserve、replace和collapse。其中,preserve表示不对元素中的任何空白字符进行处理,replace表示移除所有的空白字符,collapse表示将所有的空白字符缩减为一个单一字符。接下来,以preserve为例,学习如何对空白字符进行限定,具体示例如下。

<xs:element name="address">

<xs:simpleType>

<xs:restriction base="xs:string">

<xs:whiteSpace value="preserve"/>

</xs:restriction>

</xs:simpleType>

</xs:element>

上面的示例代码对address元素内容的空白字符进行了限定。在使用whiteSpace限定时,将值设置为“preserve”,表示这个XML处理器将不会处理该元素内容中的所有空白字符。

需要注意的是,在Schema文档中,还有很多限定的情况,比如对长度的限定,对数据类型的限定等,如果想更多的了解这些限定的使用,可以参看W3C文档。

(4)复杂类型

除简单类型之外的其他类型都是复杂类型,在定义复杂类型时,需要使用xs:complex- Content元素来定义。复杂类型的元素可以包含子元素和属性,这样的元素称为复合元素。在定义复合元素时,如果元素的开始标记和结束标记之间只包含字符数据内容,那么这样的内容是简易内容,需要使用xs:simpleContent元素来定义。反之,元素的内容都是复杂内容,需要使用xs:complexContent元素来定义。复合元素有4种基本类型,接下来,针对这4种基本类型分别进行讲解。具体如下。

1)空元素

这里的空元素指不包含内容,只包含属性的元素,具体示例如下。

<product prodid="1345" />

在上面的元素定义中,没有定义元素product的内容,这时,空元素在XML Schema文档中对应的定义方式如下所示。

<xs:element name="product">

<xs:complexType>

<xs:attribute name="prodid" type="xs:positiveInteger"/>

</xs:complexType>

</xs:element>

2)包含其他元素的元素

在XML文档中存在包含其他元素的元素,例如下面的示例代码。

<person>

<firstname>John</firstname>

<lastname>Smith</lastname>

</person>

在上面的示例代码中,元素person嵌套了两个元素,分别是firstname和lastname。这时,在Schema文档中对应的定义方式如下所示。

<xs:element name="person">

<xs:complexType>

<xs:sequence>

<xs:element name="firstname" type="xs:string"/>

<xs:element name="lastname" type="xs:string"/>

</xs:sequence>

</xs:complexType>

</xs:element>

3)仅包含文本的元素

对于仅含文本的复合元素,需要使用simpleContent元素来添加内容。在使用简易内容时,必须在simpleContent元素内定义扩展或限定,这时,需要使用extension或restriction元素来扩展或限制元素的基本简易类型。请看一个XML的简易例子,其中,“shoesize”仅包含文本,具体示例如下。

<shoesize country="france">35</shoesize>

在上面的例子中,元素 shoesize 包含了属性以及元素内容,针对这种仅包含文本的元素,需要使用extension来对元素的类型进行扩展,在Schema文档中对应的定义方式如下所示。

<xs:element name="shoesize">

<xs:complexType>

<xs:simpleContent>

<xs:extension base="xs:integer">

<xs:attribute name="country" type="xs:string" />

</xs:extension>

</xs:simpleContent>

</xs:complexType>

</xs:element>

4)包含元素和文本的元素

在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文档中对应的定义方式如下所示。

<xs:element name="letter">

<xs:complexType mixed="true">

<xs:sequence>

<xs:element name="name" type="xs:string"/>

<xs:element name="orderid" type="xs:positiveInteger"/>

<xs:element name="shipdate" type="xs:date"/>

</xs:sequence>

</xs:complexType>

</xs:element>

需要注意的是,为了使字符数据可以出现在letter元素的子元素之间,使用了mixed属性,该属性是用来规定是否允许字符数据出现在复杂类型的子元素之间,默认情况下mixed的值为false。