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

2.1 XML基础

在实际开发中,不同语言(例如Java、JavaScript等)的应用程序之间数据传递的格式不同,导致不同语言开发的应用程序在数据交换时变得很困难。为解决此问题,W3C 组织推出了一种新的数据交换标准——XML。XML 是一种通用的数据交换格式,可以使数据在各种应用程序之间轻松地实现格式交换。下面将对XML进行详细讲解。

2.1.1 XML概述

XML是一种类似于HTML的置标(标记)语言,称为可扩展置标(标记)语言。XML用于提供数据描述格式,适用于不同应用程序之间的数据交换,而且这种交换不以预先定义的一组数据结构为前提,增强了可扩展性。下面分别从什么是XML以及XML与HTML的比较这两个方面进行讲解。

1. 什么是XML

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

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

使用XML前,要了解XML文档的基本结构,然后再根据该结构创建所需的XML文档。下面通过一个XML文档描述图2-1所示的关系,如文件2-1所示。

图2-1 国、省、市之间的层次关系图

文件2-1 city.xml

1  <?xml version="1.1" encoding="UTF-8"?>
2  <中国>
3      <河北>
4           <城市>张家口</城市>
5           <城市>石家庄</城市>
6      </河北>
7      <山西>
8           <城市>太原</城市>
9           <城市>大同</城市>
10     </山西>
11 </中国>

在文件2-1中,第1行代码是XML的文档声明;第2~11行代码中<中国>、<河北>、<城市>都是用户自己创建的标签,它们都称为元素,这些元素必须成对出现,即包括开始标签和结束标签。例如,<中国>元素中的开始标签为<中国>,结束标签为</中国>。<中国>被视为整个XML文档的根元素,在它下面有两个子元素,分别是<河北>和<山西>,在这两个子元素中又分别包含两个<城市>元素。

在XML文档中,通过元素的嵌套关系可以很准确地描述具有树状层次结构的复杂信息,因此越来越多的应用程序都采用XML格式存放相关的配置信息,以便于读取和修改配置信息。

2. XML与HTML的比较

XML和HTML都是基于文本的标记语言,它们在结构上大致相同,都是以标签的形式描述信息。但实际上它们有着本质的区别,具体如下。

(1)HTML用于显示数据,XML用于传输和存储数据。

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

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

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

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

总之,XML不是HTML的升级,也不是HTML的替代产品,虽然两者有些相似,但它们的应用领域和范围完全不同。

2.1.2 XML语法

一个XML文件可以嵌套很多内容,例如文档声明、元素定义、属性定义、注释等,这些内容的编写都需要遵循一定的语法规范,下面对这些内容的语法进行详细讲解。

1. 文档声明

从XML 1.1开始,在一个完整的XML文档中,必须包含一个XML文档的声明,并且该声明必须位于文档的第一行。XML声明表示该文档是一个XML文档,以此判断需要遵循哪个XML版本的规范。

XML文档声明的语法格式如下:

<?xml version="version" encoding="value" standalone="value"?>

从上述语法格式中可以看出,文档声明以符号“<?”开头,以符号“?>”结束,中间可以声明版本信息、编码信息和文档独立性信息。下面对XML声明中的参数进行介绍,具体如下。

● version:用于指定遵循XML规范的版本号。在XML声明中必须包含version属性,且该属性必须放在XML声明中其他属性之前。

● encoding:用于指定XML文档所使用的编码集。常用的编码集有GBK、GB2312、BIG5、ISO-8859-1和UTF-8。

● standalone:用于指定该XML文档是否与一个外部文档嵌套使用,取值为yes或no。如果设置属性值为yes,说明是一个独立的XML文档,与外部文档无关联;如果设置属性值为no,说明XML文档不独立。

需要注意的是,在“<”和“?”之间、“?”和“>”之间以及第一个“?”和xml之间不能有空格。另外,在XML声明中,encoding和standalone是可选的,只有version是强制性声明。

2. 元素定义

XML文档中的主体内容都是由元素(element)组成的,元素是以树状分层结构排列的,一个元素可以嵌套在另一个元素中。XML文档中有且仅有一个顶层元素,称为文档元素或根元素。元素一般由开始标签、属性、元素内容和结束标签构成,具体示例如下:

<城市>北京</城市>

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

3. 属性定义

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

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

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

4. 注释

注释是为了便于阅读和理解,会在XML文档中插入的一些附加信息,例如作者姓名、地址或电话等,这些信息是对文档结构或文档内容的解释,不属于XML文档的内容,因此XML解析器不会处理注释内容。XML文档的注释以字符串“<!--”开始,以字符串“-->”结束。具体语法格式如下:

<!--注释信息-->

2.1.3 DTD约束

经过学习前2个小节可知,只要遵守XML的语法规定,编写一个结构良好的XML文档还是很轻松的。但是,之前所学的都是对文档结构的要求,而没有在语法语义上对文档进行约束。通过DTD约束可以比较轻松地对XML文档中的内容的语法语义进行约束,并且可以通过解析器来验证XML文档是否符合DTD约束。下面分别对什么是XML约束、什么是DTD约束、DTD的引入和DTD约束语法进行介绍。

1. 什么是XML约束

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

<?xml version="1.1" encoding="UTF-8"?>
<书架>
    <书>
        <书名>Java基础案例教程</书名>
        <作者 姓名="黑马程序员"/>
        <售价 单位="元">38</售价>
        <售价 单位="元">28</售价>
    </书>
</书架>

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

对XML文档进行约束时,同样需要遵守一定的语法规则,这种语法规则就形成了XML约束语言。

2. 什么是DTD约束

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

文件2-2 book.xml

1  <?xml version="1.1" encoding="UTF-8"?>
2   <书架>
3      <书>
4           <书名>Java基础案例教程</书名>
5           <作者>黑马程序员</作者>
6           <售价>54.00元</售价>
7      </书>
8      <书>
9           <书名>Java基础入门</书名>
10          <作者>黑马程序员</作者>
11          <售价>59.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.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.1" encoding="UTF-8"?>
2  <!DOCTYPE 书架 SYSTEM "book.dtd">
3  <书架>
4      <书>
5           <书名>Java基础案例教程</书名>
6           <作者>黑马程序员</作者>
7           <售价>54.00元</售价>
8      </书>
9      <书>
10          <书名>Java基础入门</书名>
11          <作者>黑马程序员</作者>
12          <售价>59.00元</售价>
13     </书>
14 </书架>

在文件2-4中,因为引入的是本地的DTD文件,所以在第2行代码中使用SYSTEM属性引入book.dtd文件。

如果希望引入一个公共的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.1" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE 根元素名 [
      DTD定义语句
      ……
]>

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

文件2-5 book.xml

1  <?xml version="1.1" 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          <售价>54.00元</售价>
14     </书>
15     <书>
16          <书名>Java基础入门</书名>
17          <作者>黑马程序员</作者>
18          <售价>59.00元</售价>
19     </书>
20 </书架>

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

4. DTD约束语法

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

(1)元素定义

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

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

在上面元素的定义语法格式中,包含了“元素名称”和“元素内容”。其中,“元素名称”是被约束的XML文档中的元素,“元素内容”是对元素包含内容的声明,其内容包括数据类型和符号两个部分。

元素内容共有5种内容形式,具体如下。

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

② 子元素:说明元素包含其他元素。通常用一对小括号()将元素中要嵌套的一组子元素括起来。例如,<!ELEMENT 书 (书名,作者,售价)>表示元素“书”中要嵌套“书名”“作者”“售价”这3个子元素。

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

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

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

需要注意的是,在定义元素时,元素内容可以包含一些符号,不同的符号具有不同的作用。下面介绍一些常见的符号。

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

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

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

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

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

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

(2)属性定义

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

<!ATTLIST 元素名
    属性名1 属性类型 设置说明
    属性名2 属性类型 设置说明
    ......
>

在上述语法格式中,“元素名”是属性所属元素的名字;“属性名”是属性的名称;“属性类型”则用于指定该属性属于哪种类型;“设置说明”用于说明该属性是否必须出现。

关于“设置说明”和“属性类型”的相关讲解,具体如下。

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

表2-1 属性定义的设置说明

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

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

b. Enumerated(枚举类型)。在声明属性时,可以限制属性的取值只能从一个列表中选择,这类属性属于Enumerated(枚举类型)。下面通过一个案例学习如何定义Enumerated类型的属性,如文件2-6所示。

文件2-6 enum.xml

1  <?xml version="1.1" encoding="UTF-8" standalone="yes"?>
2  <!DOCTYPE 购物篮 [
3     <!ELEMENT 购物篮 ANY>
4     <!ELEMENT 肉 EMPTY>
5     <!ATTLIST 肉 品种 (鸡肉|牛肉|猪肉|鱼肉) "鸡肉">
6  ]>
7  <购物篮>
8     <肉 品种="鱼肉"/>
9     <肉 品种="牛肉"/>
10    <肉/>
11 </购物篮>

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

c. ID。一个ID类型的属性用于唯一标识XML文档中的某个元素。ID类型的属性值必须遵守XML名称定义的规则。一个元素只能有一个ID类型的属性,而且ID类型的属性必须设置为#IMPLIED或#REQUIRED。因为ID类型属性的每一个取值都用于标识一个特定的元素,所以为ID类型的属性提供默认值,特别是固定的默认值,是毫无意义的。

下面通过一个案例学习如何定义一个ID类型的属性,如文件2-7所示。

文件2-7 id.xml

1  <?xml version="1.1" 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中,第2~8行代码声明了联系人列表的约束。其中,第7行代码是将元素为<联系人>的“编号”属性设置为ID #REQUIRED,说明每个联系人都必须有一个编号,且编号是唯一的。如此一来,通过编号就可以找到唯一对应的联系人了。

d. IDREF和IDREFS。在文件2-7中,张三和李四两个联系人元素之间没有任何关联,如果要将张三和李四两个联系人元素关联起来,这时可以使用IDREF类型,IDREF类型的属性值必须为一个已经存在的ID类型的属性值。IDREF类型使这两个联系人之间建立一对一的关系。下面通过一个案例学习IDREF类型的使用,如文件2-8所示。

文件2-8 idref.xml

1  <?xml version="1.1" 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中,第2~10行代码声明了联系人列表的约束,其中第7~9行代码是为元素<联系人列表>的子元素<联系人>增加了一个名称为“上司”的属性约束,并且将该属性的类型设置为IDREF;第16~19行代码是在第二个<联系人>元素中,将“上司”属性设置为第一个联系人的编号属性值。如此一来,就在两个联系人元素之间形成了对应关系,即李四的上司为张三。

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

下面通过一个案例学习IDREFS的使用,如文件2-9所示。

文件2-9 library.xml

1  <?xml version="1.1" encoding="UTF-8"?>
2  <!DOCTYPE library[
3     <!ELEMENT library (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中,第11行代码是将元素<book>中属性名为“bookid”的属性设置为ID类型,第13行代码是将元素<person>中名为“borrowed”的属性设置为IDREFS类型。从第15~37行代码中可以看出,张三借阅了《Java基础案例教程》和《Java基础入门》这两本书,而李四则借阅了《Java基础案例教程》《Java Web程序开发入门》和《Java基础入门》这三本书。

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

2.1.4 Schema约束

XML有非常高的合法性要求,但DTD约束的语法相当复杂,自成一个体系,并且它不符合XML文件的标准。XML Schema则不同,它与XML有着同样的合法性验证机制。下面对Schema约束进行讲解。

1. 什么是Schema约束

2.1.3小节学习了DTD约束,同DTD一样,XML Schema也是一种用于定义和描述XML文档结构与内容的模式语言,它的出现克服了DTD 的局限性。与DTD相比,XML Schema具有以下显著优点。

(1)DTD采用的是非XML语法格式,缺乏对文档结构、元素、数据类型等全面的描述。XML Schema采用的是XML语法格式,而且它本身也是一种XML文档。因此,XML Schema语法格式比DTD更好理解。

(2)XML有非常高的合法性要求,XML DTD对XML的描述往往也被用作验证XML合法性的基础,但是XML DTD本身的合法性却缺少较好的验证机制,必须独立处理。XML Schema则不同,它与XML有着同样的合法性验证机制。

(3)XML Schema对名称空间支持得非常好,而DTD几乎不支持名称空间。

(4)DTD支持的数据类型非常有限。例如,DTD可以指定元素中必须包含字符文本(PCDATA),但无法指定元素中必须包含非负整数(Nonnegative Integer),而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是一个Schema文档,以.xsd作为后缀名。在文件2-10中,第1行代码是文档声明,第2行代码中以xs:schema作为根元素,表示模式定义的开始。根元素上必须声明名称空间xmlns:xs="http://www. w3.org/2001/XMLSchema",因为根元素xs:schema的属性都在http://www.w3.org/2001/XMLSchema名称空间中。第3行代码声明的名称为“root”的元素必须来自名称空间http://www.w3.org/ 2001/XMLSchema且为String类型。

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.1" encoding="UTF-8"?>
2  <it315:书架 xmlns:it315="http://www.it315.org/xmlbook/schema">
3      <it315:书>
4           <it315:书名> Java基础案例教程</it315:书名>
5           <it315:作者>黑马程序员</it315:作者>
6           <it315:售价>54.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绑定,只能用于XML 1.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之间用空白符分隔。其中,第一个URI是名称空间的名称,第二个URI是文档的位置。下面通过一个案例演示如何使用名称空间引入XML Schema文档,如文件2-12所示。

文件2-12 book.xml

1  <?xml version="1.1" 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           <书名>Java基础案例教程</书名>
8           <作者>黑马程序员</作者>
9           <售价>54.00元</售价>
10     </书>
11 </书架>

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

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

文件2-13 xmlbook01.xml

1  <?xml version="1.1" 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          <书名>Java基础案例教程</书名>
11          <作者>黑马程序员</作者>
12          <售价 demo:币种="人民币">54.00元</售价>
13     </书>
14 </书架>

(2)通过xsi:noNamespaceSchemaLocation属性直接指定

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

文件2-14 xmlbook02.xml

1  <?xml version="1.1" encoding="UTF-8"?>
2  <书架 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3       xsi:noNamespaceSchemaLocation="xmlbook.xsd">
4      <书>
5           <书名>Java基础案例教程</书名>
6           <作者>黑马程序员</作者>
7           <售价>54.00元</售价>
8      </书>
9  </书架>

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

4. Schema语法

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

(1)元素定义

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

<xs:element name="名称" type="类型"/>

在上述语法格式中,element 用于声明一个元素,名称是指元素的名称,类型是指元素的数据类型。在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示例代码中定义了三个元素,这三个元素对应的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元素。下面通过以下几种情况详细介绍如何对简单类型元素的内容进行限定,具体如下。

① 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元素限制了年龄值的范围。

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

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

④ xs:restriction元素对空白字符的限定。

在XML文档中,空白字符比较特殊,如果需要对空白字符(Whitespace Characters)进行处理,可以使用whiteSpace元素。whiteSpace元素有三个属性值可以设定,分别是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:complexContent 元素来定义。复杂类型的元素可以包含子元素和属性,这样的元素称为复合元素。在定义复合元素时,如果元素的开始标签和结束标签之间只包含字符数据内容,那么这样的内容是简易内容,需要使用xs:simpleContent元素来定义。反之,元素的内容都是复杂内容,需要使用xs:complexContent元素来定义。复合类型元素有4种基本类型,下面对这4种基本类型进行讲解。

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

<product prodid="1345" />

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

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

② 包含其他元素的元素。对于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>

③ 仅包含文本的元素。对于仅包含文本的复合元素,需要使用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>

④ 包含元素和文本的元素。

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