Schema概述
XML Schema是2001年5月正式发布的W3C(万维网联盟)的推荐标准,经过数年的大规模讨论和开发如今终于奠定下来,成为全球公认的XML环境下首选的数据建模工具。
使用DTD虽然带来较大的方便,但是,DTD存在一些缺陷:一是它是用不同于XML的语言编写的,需要不同的分析器技术。这增加了工具开发商的负担,削减了软件瘦身的可能性,此外开发人员需要多学一门语言及其语法。而XML Schema是按标准XML规则编写的,更容易掌握。 二是DTD不支持名称空间。随着计算日益以XML为中心,信息的相互联系变得日益普及与深入,名称空间作用也将凸现。三是DTD在支持继承和子类方面的局限性,由于面向对象技术的出现,对继承和子类的支持已成为软件技术领域的主流概念。最后,DTD没有数据类型的概念,无法对特定元素施加数据类型,对强制性结构化无计可施,比如如何规定名为Date的数据必须包含有效值。
这些就要依靠XML Schema了。XML Schema不仅可以定义XML文档的结构而且还允许约束文档的内容,这就不同于DTD了。另外,一个 XML Schema自身就是一个XML文档,其基于标签的语法比DTD中的特殊字符要清楚多了。XML Schema正是针对这些DTD的缺点而设计的,它完全使用XML作为描述手段,具有很强的描述能力、扩展能力和处理维护能力。
XML Schema是用一套预先规定的XML元素和属性创建的,这些元素和属性定义了文档的结构和内容模式。
XML Schema也是Web Services技术中需要使用的一个基本工具,然而并不是XML Schema的所有特性都会被广泛地使用,因此,将不对XML Schema规范做系统的介绍。目前主要有两种重要的模式:Microsoft XML Schema和W3C XML Schema,本章主要讨论W3C XML Schema
在下面的例子中,通过使用出现在schema元素中的名称空间声明xmlns:xsd= "http://www.w3.org/ 2001/XMLSchema",使得模式文档中的每一个元素都有一个与XML Schema名称空间相联名称空间前缀xsd。尽管在语法上,可以使用任意的前缀形式,但是,名称空间前缀xsd被约定用于表示XML Schema名称空间。由于使用同样的前缀,所以同样的关联就会出现在内置的简单类型的名字中,例如xsd:string。这种形式关联的目的是用来表示当前的元素或简单类型是属于XML Schema语言的内置定义的,而不是属于模式文档作者自己的词汇表的。为了在这里清楚并且简单地表示,仅提及元素的名字和简单类型名,而忽略它们的前缀xsd。
Schema的格式和使用
一个XSDL(XML Schema Definition Language)文档由元素、属性、名称空间和XML文档中的其它节点构成的,并且至少要包含:schema根元素和XML模式名称空间的定义、元素定义。
5.2.1 简单实例
【例5.1】关于书籍信息的XML文档,code5_1.xml。
<?xml version="1.0" encoding="gb2312"?>
<book isbn="0-764-58007-8">
<title>三国演义</title>
<author>罗贯中</author>
<price>80.00 </price>
<resume>滚滚长江东逝水,浪花淘尽英雄。是非成败转头空:青山依旧在,几度夕阳红。白发渔樵江渚上,惯看秋月春风。一壶浊酒喜相逢:古今多少事,都付笑谈中。
</resume>
<recommendation>经典好书</recommendation>
<publish>
<publisher>文艺出版社</publisher>
<pubdate>1998.10</pubdate>
</publish>
</book>
如何写这个XML文档的Schema呢?可以简单的依照它的结构来定义它的每个元素。首先加入一个xsd:schema元素。
<?xml version="1.0" encoding=" gb2312"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2000/10/XMLSchema">
每个Schema文档都是从schema元素开始,并且只能有一个,用以控制Schema所使用的名称空间和另外几个选项,现在的Schema标准有好几种,这里决定它所采用的标准,因此是非常重要的!
对应着XML文档的book标记,同样在Schema文档中也定义一个名为book的元素(element)。因为这个元素有属性(attributes)和非文本的子元素(non-text children),因此认为它是一个复杂类型(complexType)。而对于简单类型,只能有值,不能有子元素或者属性。同时还注意到book元素下的子元素,都是按照一定的顺序排列,因此使用顺序元素(sequence element)来描述。
<xsd:element name="book">
<xsd:complexType>
<xsd:sequence>
顺序元素(sequence element)是一个定义子元素排列顺序的元素,在下面的章节,还将其它类似的元素,如选择(choice)和全选(all)。
接着定义title和author,都是xsd:string类型的简单元素,因为没有属性(attributes)或者子元素。xsd:string是一个已经在名域中预定义了的XML Schema类型中的一个。
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="author" type="xsd:string"/>
<xsd:element name="price" type="xsd:integer" />
<xsd:element name="resume" type="xsd:string" />
<xsd:element name="recommendation" type="xs:string" />
接着,来处理publish元素,它也是一个复杂类型。注意它的基数的定义。
<xsd:element name="publish" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
同其它Schema定义语言不一样,W3C XML Schema允许定义一个元素的使用基数,能指定元素的minOccurs(最小数)和maxOccurs(最大数)。这里设置maxOccurs为unbounded,这意味能有任意多的publish元素。这两个属性的默认值都是1。下面,用同样的方法定义其它的子元素。
<xsd:element name="publisher" type="xsd:string" />
<xsd:element name="pubdate" type="xsd:date" />
下面封闭complexType和element等元素:
</xsd:sequence>
</xsd:complexType>
</xsd:element>
这样 publish"元素就完成了。然后封闭book的sequence元素。
</xsd:sequence>
现在,声明book元素的属性,通常是在最后这么做。这样做没有什么特别的原因,只不过W3C XML Schema工作组认为在元素后定义属性感觉更加自然。
<xsd:attribute name="isbn" type="xsd:string"/>
最后,封闭所有剩下的元素。
</xsd:complexType>
</xsd:element>
</xsd:schema>
至此,一个Schema已经完成了。在这其中,最关键的在于根据文档的上下关系来定义每一个元素和属性,并且允许有多个同名元素带有不同的内容。为了这个目的,W3C XML Schema 被设计成一种有作用域的语言,它的每一个定义只能被它定义的子元素所看见。
【例5.2】关于books.xml的模式定义的完整例子,code5_2.xsd
<?xml version="1.0" encoding=gb2312" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="book">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="author" type="xsd:string"/>
<xsd:element name="price" type="xsd:integer" />
<xsd:element name="resume" type="xsd:string" />
<xsd:element name="recommendation" type="xsd:string" />
<xsd:element name="publish" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="publisher" type="xsd:string" />
<xsd:element name="pubdate" type="xsd:date" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="isbn" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
5.2.2 Schema的使用
符合某个模式的文档称为实例。实例可以根据特定的模式进行验证。需要声明xml文档的schema实例名称空间(xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"),并把名称空间映射到前缀xsi。实例与模式之间有多对多的关系。一个模式可以描述多个有效的实例(通过使用不同的根元素类型来实现),同样,一个实例也可以被多个模式描述。例如一个实例可能拥有多个模式,它们有不同的验证级别。其中一个模式可能只验证结构,而另一个模式则根据数据类型来检查每一个数据项。
1. Schema作用
Schema文档就是用来验证XML文档的正确性,用来判断实例是否符合模式中所描述的所有约束。涉及到检查实例中所有的元素和属性。
Schema主要检验如下内容:
· 验证数据的显示格式正确及是否超出值的范围。
· 验证所有必需的信息都存在。
· 确保不同使用者对文档理解的方式相同
除了对XML文档的验证外,Schema文档还在一定程度上扩充实例:
· 为元素和属性添加默认值和固定值。
· 使具有简单类型的元素和属性值中的空白符规范化。
2. Schema引用
一个模式可能由多个模式文档构成。多个模式文档通过包含或导入机制来形成模式,当其他模式文档与主模式文档具有相同的目标名称空间时,需要使用包含。当模式文档之间各自拥有不同的目标名称空间时,需要使用导入。下面的例子建立一个单独用来验证一个实例的模式文档。
【例5.3】关于多个模式文档通过包含实现定义的例子,code5_3.xsd
<?xml version="1.0" encoding="gb2312" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://example.org/ord"
targetNamespace="http://example.org/ord">
<xsd:include schemaLocation="moreBookInfo.xsd"/>
<xsd:import namespace="http://example.org/ord" schemaLocation="publish.xsd" />
<!--...-->
</xsd:schema>
要验证XML文档,必须指定Schema文档的位置。模式的位置可以利用带有名称空间的模式的xsi:schemaLocation属性以及不带名称空间的XML模式的xsi:noNamespaceSchemaLocation属性来指定,它们位于根/顶级元素中或XML文档的任何其他元素中。
当schema文档不包括targetNamespace属性时,应当通过XML文档根元素的noNamespaceSchemaLocation属性及W3C的schema实例名称空间(xmlns:xsi="http://www.w3.org/2001/XMLScheam-instance")来引用XML schema文件。针对上面的示例修改如下:
<?xml version="1.0" encoding="gb2312" ?>
<book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="books.xsd"
isbn="0-764-58007-8">
<!--...-->
</book>
然而,如果Schema文件包含了一个targetNamespace 属性,在XML文档中就将通过schemaLocation属性而不是noNamespaceSchemaLocation属性来引用schema文档。而且,这个属性所指定的值必须是完整的。它需要包含以空格分开的两部分,前一部分是URI,这个URI与schema文档的targetNamespace属性内部引用的URI是一致的;后一部分是schema文件完整路径及名称。另外,XML文档的根元素也必须声明schema实例名称空间(xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance")。Schema文档如下所示:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://tempuri.org/book">
<!--...-->
</xsd:schema>
则修改上面的实例为:
<?xml version="1.0" encoding="gb2312" ?>
<book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tempuri.org/book book.xsd"
xmlns="http://tempuri.org/book"
isbn="0-764-58007-8">
<!--...-->
</book>
5.3 Schema元素声明
元素是创建XML文档的主要构建材料。在W3C XML Schema中,元素通过使用<element>元素实现。元素声明用于给元素指定元素类型名称、内容和数据类型等属性。在XSDL中,元素声明可以是全局的,也可以是局部的。
5.3.1 Schema根元素
在XSD中必须定义一个且只能定义一个schema根元素。根元素不但表明了文档类型,而且还包括模式的约束、XML模式名称空间的定义,其他名称空间的定义、版本信息、语言信息和其他一些属性。定义如下:
<?xml version="1.0" encoding="gb2312" ?>
<xsd: schema name="mySchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
…
</xsd: schema>
其中属性name指定Schema名称,也可以不需要。xmlns指定所属名称空间,紧跟在后面的xsd则是该名称空间的名称,名称空间” http://www.w3.org/2001/XMLSchema“被映射到xsd前缀,在后面将详细说明名称空间。
5.3.2 element元素
XSD中的元素是利用element标识符来声明的。其中name属性是元素的名字,type属性是元素值的类型,在这里可以是XML Schema中内置的数据类型或其他类型。具体定义如下:
<xsd:element name="author" type="xsd:string" />
name是元素类型的名称,必须是以字母或下划线开头,而且只能够包含字母、数字、下划线、连接符及句号。type属性是必要的,说明元素的数据类型。
下面的例子定义一个全局元素声明:author,为简单字符串类型。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="author" type="xsd:string" />
</xsd:schema>
与以上文档对应的有效XML文档如下:
<?xml version="1.0" encoding="gb2312" ?>
<author>罗贯中</author>
在元素的定义中还有2个属性:minOccurs和maxOccurs。其中minOccurs定义了该元素在父元素中出现的最少次数(默认为1,值为大于等于0的整数),maxOccurs定义了该元素在父元素中出现的最多次数(默认为1,值为大于等于0的整数)。在maxOccurs中可以把值设置为unbounded,表示对元素出现的最多次数没有限制。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="author" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
</xsd:schema>
表示元素author的类型为string,出现的次数最少为0(就是可选),最多不限制。
一般来说,如果元素声明出现在Schema文档的顶级结构中,也就是说,它的父元素是schema,那么这些元素为全局元素。
相反,局部元素声明只出现在复杂类型定义内部,局部元素声明只在该类型定义中使用,而不被其他复杂类型引用或在替换组中使用。不同的复杂类型,可以有相同元素类型名称的局部元素。
【例5.4】显示title、author和price三种局部元素声明的模式文档,code5_4.xsd
<?xml version="1.0" encoding="gb2312"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2000/10/XMLSchema">
<xsd:complexType name="book">
<xsd:sequence>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="author" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="price" type="xsd:integer"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
5.3.3 设置默认值和固定值
默认值和固定值通过给空元素增加值的方式来扩展实例。如果文档中存在空的元素,模式处理器根据模式文档的定义,会插入默认值或固定值。在XSDL中,默认值和固定值分别通过default和fixed属性设置。两个属性只能出现其中之一,它们是互斥的。
如果元素为空,就填入默认值。下例中,声明了city元素,并指定了默认值为“佚名”。
<xsd:element name="author" type="xsd:string" default="佚名"/>
必须注意的是:元素声明中“空”的定义根据数据类型不同而有所不同。某些数据类型允许空值,包括string等。任何允许空字符串值的类型,元素都不会认为是空的,从而对于字符串类型元素,默认值不会被填充。相反,integer数据类型的空元素通常会被认为是空的,从而将填入默认值。此外,如果元素的xsi:nil属性被设置为true,那么就不会插入它的默认值。
<?xml version="1.0" encoding="gb2312"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2000/10/XMLSchema">
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="author" type="xsd:string" default="N/A" />
<xsd:element name="price" type="xsd:integer" default="30" />
</xsd:schema>
元素的默认值行为见表5.1。
表5.1元素的默认值行为
情 况 | 扩充实例结果 | 扩充之前 | 扩充之后 |
指定值 | 保持原始值 | <author>罗贯中</author> | <author>罗贯中</author> |
空元素(integer) | 填充值 | <price></price> | <price>30</price> |
空元素(string) | 没有填充值 | <author></author> | <author></author> |
元素为空 | 没有填充值 | <price xsi:nil="true"/> | <price xsi:nil="true"/> |
固定值与默认值在相同的情况下添加,它们的差别仅在于如果元素拥有一个值,则该值必须和固定值相等。当模式解析器确定元素值和固定值实际上是否相等时,会考虑到元素的数据类型。price元素的数据类型为integer,所以整数1的所有形式在实例中都会被接受,包括01、+1和周围包含空白符的1。相反,对于author元素具有数据类型string。字符串“01“是无效的,因为与字符串“1“并不相等。
<xsd:element name="author" type="xsd:string" fixed="1" />
<xsd:element name="price" type="xsd:integer" fixed="1" />
按照以上定义,元素的固定值行为见表5.2。
表5.2 元素的固定值行为
有效实例 | 无效实例 |
<price>1</price> | <price>2</price> |
<price>01</price> | <author>01</author> |
<price>+1</price> | <author>+1</author> |
<price></price> | <author></author> |
<price/> | <author> </author> |
<author>1</author> | <author /> |
5.3.4引用是利用element标记符的ref属性实现的。主要适用于避免在文档中多次定义同一个元素,应当将经常使用的元素定义为根元素的子元素,即为全局元素,这样方便在文档的任何地方引用它。如每本书籍都有作者,其它产品也会有作者,因此可以把author属性声明为全局元素,供文档中多处引用。
【例5.5】引用元素定义的模式文档,code5_5.xsd
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="author" type="xsd:string"/>
<xsd:element name="book">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="author" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
在这里还可以为某个定义的元素起一个别名,主要是利用element标识符中的属性substitutionGroup实现的。
方法如下:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="writer" type="xsd:string" substitutionGroup="author" />
<xsd:element name="author" type="xsd:string"/>
<xsd:element name="book">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="author" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
以上文档对应的有效XML文档如下:
<?xml version="1.0"?>
<book>
<author>罗贯中</author>
</book>
或者:
<?xml version="1.0"?>
<book>
<writer>string</writer>
</book>
Schema属性声明
属性声明用于命名属性并使之与某个特定的简单类型相关联。在XSDL中,实现的方法是使用attribute元素。在XML Schema文档中可以按照定义元素的方法定义属性,但受限制的程度较高。它们只能是简单类型,只能包含文本,且没有子属性。属性是没有顺序的,而元素是有顺序的。
使用属性十分简练,元素的功能比属性强大,但在某些场合属性是非常有用的。通常,对于元数据使用属性,而对于数据则使用元素。如,用属性描述单位、语言或元素值的时间相依性。
创建属性
定义属性方法如下:
<xsd:attribute name="isbn" type="xsd:string"/>
该语句定义了一个名为isbn的属性,它的值必须是一个字符类型。
属性也分全局和局部属性。全局声明的属性是schema元素的子元素,在模式文档中必须是唯一的。复杂类型的元素,使用ref属性通过名称来引用属性。局部属性声明只出现在复杂类型定义中,它们仅能在类型定义中使用,而不能被其他类型重用。下面的例子显示了两个属性——isbn和amount 的全局声明,然后定义了一个复杂类型,使用ref属性通过名称来引用这两个属性。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:attribute name="isbn" type="xsd:string" />
<xsd:attribute name="amount" type="xsd:integer" />
<xsd:complexType name="book">
<xsd:attribute ref="isbn" use="required" />
<xsd:attribute ref="amount">
</xsd:complexType>
</xsd:schema>
use属性用于指示属性是必需的还是可选的,它出现在属性引用而不是属性声明中,这是因为它关系到属性在复杂类型中的出现,而不是属性本身。上面的例子定义的是全局的属性定义方式,如果要在复杂类型里声明属性,可以参照下面的例子:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="book">
<xsd:attribute name="isbn" type="xsd:string" use="required" />
<xsd:attribute name="amount" type="xsd:integer" />
</xsd:complexType>
</xsd:schema>rbut
上面的例子描述了isbn和amount两个属性的局部声明,它们完全出现在复杂类型定义中。局部声明的属性名称作用范围仅限于复杂类型内,在同一个复杂类型定义中,两个属性使用相同的限定名称是非法的。只有当属性会被多个名称空间的多个元素声明使用到时,才提倡将该属性声明为全局属性。
为属性指派类型
所有的属性声明都把属性指定为某种简单类型。所有的属性都具有简单类型而不是复杂类型,因为它们本身不能有子元素和属性。
属性声明有三种方式:
1. 在属性声明中通过用type属性指定命名简单类型。它可以是内置类型,也可以是用户自定义类型。
2. 通过指定simpleType子属性来指定匿名类型。
3. 既没有type属性,又没有simpleType子属性,从而不指定特定类型。在这种情况下,属性的类型为anySimpleType,它可以拥有任何值,只有它是结构合理的XML文档。
下例显示四个属性的声明,采用了不同的类型指定方法。定义了grade属性并赋予gradeType类型,amount属性指定了内置类型integer,同时使用内嵌的匿名简单类型来声明bookcategory属性,第四个属性anything没有指定特定的类型。
<xsd:attribute name="amount" type="xsd:integer" />
<xsd:attribute name="grade" type="gradeType" />
<xsd:attribute name="bookcategory">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="文艺"/>
<xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="anything"/>
属性的默认值和固定值
对于属性来说,也可以通过默认值和固定值的方式增加未出现的属性来扩充实例。定义和扩充的方式与元素一致。在XSDL中,默认值和固定值分别通过default和fixed属性设置。两个属性只能出现其中之一,它们是互斥的。
如果属性在元素中缺失,它的默认值将会被填入。如果属性出现,且包含任意值,它将保持该值不变。下面的例子显示了book元素的声明,它包含一个属性amount,该属性被指定了默认值。
<xsd:element name="book">
<xsd:complexType>
<xsd:attribute name="amount" type="xsd:integer" default="100"/>
</xsd:complexType>
</xsd:element>
固定值与默认值在基本一样的情况下被插入,区别在于,其值应和固定值相等。同时也会考虑属性的类型。
<xsd:element name="book">
<xsd:complexType>
<xsd:attribute name="isbn" type="xsd:string" fixed="100"/>
<xsd:attribute name="amount" type="xsd:integer" fixed="100" />
</xsd:complexType>
</xsd:element>
Schema的数据类型
W3C XML Schema可以把XML文档中的元素声明为特定的类型,准许解析器检查文档的内容及其结构。XML Schema定义了两种主要的数据类型:预定义简单类型和复杂类型。这两种数据类型之间的主要区别是复杂类型可以象数据一样包含其他元素而简单类型则只能包含数据。简单类型给予了XML Schema低级类型检查能力。
5.5.1 简单类型
元素和属性声明都可以使用简单类型来描述数据类型。
1. 简单类型的种类
原子类型(不可分割的类型,如string, integer等系统内建的类型)、列表类型、联合类型合起来统一称为简单类型。XML Schema具有低级类型检查能力,允许把元素定义为表5.3中的任何类型之一。赋予简单类型的元素具有字符类型内容,但没有子元素或属性。
· 原子类型具有不可分割的值,如10和large
· 列表类型的值为用空白符隔开的原子值列表,如<availableSizes>10 large 2</availableSizes>
· 联合类型的值可以是原子值,也可以是列表值。它们的区别在于该类型的有效值集是两个或多个其他简单类型值空间的联合。如要对于学生的成绩评定等级,可以定义一个联合类型,它允许的值既可以是0到100的整数,也可以是优、良、合格或不合格。
表5.3 XML Schema支持的部分简单类型
内建类型 | 定义 |
string | 字符串数据,如"online book shop" |
boolean | 二元类型的True或者False。 |
date | 表示日期,格式是CCYY-MM-DD。 |
dateTime | 表示当前时间,由日期和时间组成, 如11/18/80,10:00am |
time | 24小时格式的时间可根据时区调节。 |
decimal | 任意精度和位数的十进制数,如27.93 |
integer | 整数,如34 |
float | 标准的32位浮点数,如11.87e-2 |
在前面一些例子中,有几个元素和属性被声明为简单类型。其中一些简单类型如 string 和integer是XML Schema中内置的,其它的一些则是源于(如果使用对象技术的语言就是继承)内置的类型。
除此之外,新的简单类型可以通过从现有的简单类型(内置的简单类型以及源于内置简单类型的简单类型)引出定义。通常,通过重新约束一个现存的简单类型来引出一个新的简单类型。换句话说,新类型的合法值范围是现有类型的值范围的子集。使用simpleType元素来定义和命名新的简单类型,使用restriction元素来指出现有的基类型,并且用它来标识约束值范围的细节。
假设希望建立一个新的整数类型称为myInteger,它的值范围为10000到99999。那么定义应当基于简单类型integer,然后定义它的值范围为10000到99999。为了定义myInteger,这样来约束integer的范围,示例如下:
<xsd:simpleType name="myInteger">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="10000"/>
<xsd:maxInclusive value="99999"/>
</xsd:restriction>
</xsd:simpleType>
这个例子显示了由一个基本类型定义和两个值域区间方面描述的组合,通过这三个要素对myInteger实施定义。
2. 简单类型命名定义
简单类型既可以为命名简单类型又可以为匿名简单类型。命名简单类型总是在全局定义,而且要求在模式的数据类型中具有唯一名称。类型的名称必须为XML无冒号名称,即必须是字母或下划线开始,只能包含字、数字、下划线、连字符和句号。如上面的例子,简单类型名为myInteger。
这种类型的模式构造非常直截了当,但有些不实用。特别是如果定义了许多只应用一次而且包含非常少约束的类型,在这种情况下,一个类型应该能够被更简单的定义。这样的简单定义通常的形式是一个节省了名称和外部引用开销的匿名类型。
在下面的示例中,publish元素声明使用了匿名类型定义。一般的来说,通过元素中是否包含“type=”这个属性可以判断匿名元素定义(或者是匿名属性定义)。如果出现无名称的类型定义,也可以认为是匿名元素(属性)定义。
<xsd:element name="publish">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:maxLength value="40"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
3. 简单类型的限制
每个简单类型都是从另一个简单类型派生而来,这另一个简单类型称为基类型。派生可以从原子类型,也可以从用户定义的简单类型派生。上面的例子都从内置的原子类型派生成新的简单类型。所有新的简单类型都以某种方式限制其基类型的值空间。下面的例子从上面定义的myInteger类型进一步限制:
<xsd:simpleType name="bookInteger">
<xsd:restriction base="xsd:myInteger">
<xsd:minInclusive value="23"/>
<xsd:maxInclusive value="2046"/>
</xsd:restriction>
</xsd:simpleType>
简单类型定义时,都是通过restriction元素来定义限制,通过base属性来规定一种基类型。在restriction内,可以以任何顺序指定任何面(facet),来对类型取值进一步限制。根据面的不同,这个元素或属性具有不同的有效值。表5.4是XML Schema面的定义。
对每个内置的原子类型,都有一套定义时可用的面。如果某个面适用于某种原子类型,那么也适用于派生于该类型的简单类型。这里有必要举例说明枚举的简单类型定义,下面的例子定义了一个简单类型category,用于说明书籍的类别:
<xsd:simpleType name="category">
<xsd:restriction base="xsd:token">
<xsd:enumeration value="小说"/>
<xsd: enumeration value="散文"/>
<xsd: enumeration value="传记"/>
<xsd: enumeration value="诗歌"/>
<xsd: enumeration value="武侠"/>
<xsd: enumeration value="纪实"/>
</xsd:restriction>
</xsd:simpleType>
表5.4 XML Schema面的定义
面 | 意 义 |
minExclusive | 值必须大于x,相对就有maxExclusive |
minInclusive | 值必须大于等于x,相对就有maxInclusive |
length | 值的长度必须等于x |
minLength | 值的长度必须大于等于x,相对就有maxLength |
totalDigits | 有效的数字的位数必须小于等于x |
fractionDigits | 小数位数必须小于等于x |
whiteSpace | 应该保留、替换或压缩whitespace,决定于x |
enumeration | x是一个有效值 |
pattern | x是值可以匹配一个正则表达式 |
4. 列表类型
除了使用简单类型中描述的原子类型,XML Schema还定义了其它两种简单类型:列表类型和联合类型。
列表类型是由一组原子类型组成,因此它的每一个部分(原子)本身都是有意义的,定义时以空格隔开原子值的列表。举例来说category是个列表类型。这个类型的元素将是NMTOKEN的列表,不同的NMTOKEN值间使用空格分隔,如“小说 散文 传记”。XML Schema有三个内置的列表类型,它们是NMTOKENS、IDREFS和ENTITIES。
除了使用内置的列表类型之外,还可以通过引用现有的原子类型建立新的列表类型(不可以使用现有的列表类型来建立新的列表类型,也不能使用复合类型来建立新的列表类型)。举例来说,可以建立一个名为myInteger的列表类型,并在实例文档中使用它。其中示例中的后半部分即为实例文档中与列表类型listOfMyIntType相一致的实例元素myInt。
<!-- Schema Fragment -->
<xsd:simpleType name="listOfMyIntType">
<xsd:list itemType="myInteger"/>
</xsd:simpleType>
<xsd:element name="myInt" type="listOfMyIntType" />
<!-- Instance Fragment -->
<myInt>20003 15037 95977 95945</myInt>
一些用于描述的参数能够被应用到列表类型的定义中,它们是length、minLength、maxLength和enumeration。举例来说,如果想定义一个列表类型,这个列表类型正好包含了六个分类项名。首先从category类型定义一个新的列表类型,称为cateList,然后通过限制cateList导出只有三个项的threeBookCate类型。具体的定义可参见下例。
<!-- Schema Fragment -->
<xsd:simpleType name="cateList">
<xsd:list itemType="category"/>
</xsd:simpleType>
<xsd:simpleType name="threeBookCate">
<xsd:restriction base="cateList">
<xsd:length value="3"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="threeBookCategory" type="threeBookCate" />
<!-- Instance Fragment -->
<threeBookCategory>小说 散文 传记</threeBookCategory>
类型为threeBookCate的元素必须有三个项,它们中的每一个项必须是一个枚举类型category的原子类型(枚举类型将在后面介绍),在示例的后半部分的实例文档中就是一个具体的应用例子。
需要注意的是,从原子类型string可以导出一个列表类型,然而,在一个string中也许会带有空格,而空格在一个列表类型实例中是作为分隔符使用的。所以当在使用基类型为string的列表类型时,应当格外小心。举例来说,假设定义了一个length取值为3的列表类型,同时这个列表类型是基于类型string。下面由三个元素组成的列表是合法的:”Asia Europe Africa”;而由三个元素这样组成的列表是不合法的:”Asia Europe Latin America”。即使”Latin America”在列表外可以作为单独的一个字符串存在,但当它包含在列表中,在Latin和America之间的空格使得第四个项被有效地生成了,因此后面的例子不符合只有三个项的列表类型定义。
5. 联合类型
应用原子类型和列表类型,一个元素或者属性的值可以为一个或者多个原子类型(列表类型)的实例。与之相比较,一个应用联合类型(Union Type)包含了多个原子类型或者列表类型,而应用了联合类型的元素或是属性的值可以是这些原子类型或列表类型中的一个类型实例。为了说明这一点,建立一个用于表示学生成绩的由评定等级或者数字列表的联合类型。gradeUnion联合类型由一个原子类型和一个列表类型构成。
<!-- Schema Fragment -->
<xsd:simpleType name="gradeUnion">
<xsd:union>
<xsd:simpleType name="scoreInteger">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="0"/>
<xsd:maxInclusive value="100"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="gradeType">
<xsd:restriction base="xsd:token">
<xsd:enumeration value="优"/>
<xsd:enumeration value="良"/>
<xsd:enumeration value="及格"/>
<xsd:enumeration value="不及格"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:union>
</xsd:simpleType>
组成联合类型的简单类型称为它的成员类型。成员类型必须是简单类型,不存在复杂类型的联合。除了直接用简单类型来规定成员类型外,还可以使用union元素的memberTypes属性来规定成员类型。假定已经在模式文档的其他地方定义了gradeType和scoreInteger,改写上面的例子:
<!-- Schema Fragment -->
<xsd:simpleType name="gradeUnion">
<xsd:union memberTypes="scoreType gradeType"/>
</xsd:simpleType>
<xsd:element name="stuScore" type="gradeUnion"/>
<!-- Instance Fragment -->
<stuScore>87</stuScore >
< stuScore >优</stuScore >
当在定义一个联合类型时,元素union的memberTypes属性的值包含了联合类型中所有类型的列表。现在,假定声明了一个gradeUnion类型的元素,称为stuScore,stuScore元素有效的实例可参见上面实例。
此外,对于联合类型而言,还有两个描述性质的参数pattern和enumeration也可以应需要使用。
复杂类型
复杂类型的元素拥有子元素和属性,也可以有字符内容。复杂类型和简单类型之间最根本的区别就是:复杂类型的内容中可以包含其他元素或属性,但简单类型既不能包含子元素,也不能带有任何属性。复杂类型有四种不同的内容类型:简单类型、纯元素类型、混合类型及空类型。下例中给出了具有复杂类型的元素:book是带有属性的元素、bookinfo是包含子元素的纯元素复杂类型、chapter是混合类型的复杂类型元素以及内容为空的元素price。它们分别属于下面要描述的4种不同的内容类型。
<?xml version="1.0" encoding="gb2312" ?>
<book isbn="0-764-58007-8">no.1</book>
<bookinfo>
<title>三国演义</title>
<author >罗贯中</author>
</bookinfo>
<chapter>宴桃园豪杰三结义 斩黄巾英雄首立功
<para>话说天下大势,分久必合,合久必分。</para>
</chapter>
<price value="30"/>
复杂类型由complexType元素创建,同简单类型一样,复杂类型可以命名,也可以是匿名的。命名的复杂类型可以被多个元素声明使用。一般在全局范围内定义。相反,匿名复杂类型完全在元素声明中进行定义,而且只可以被该声明使用一次。有三种不同的方法:
· 单个complexContent子元素,可以使用它从一个复杂类型派生出新的复杂类型。
· 单个simpleContent子元素,用于从简单类型派生复杂类型
· 组(group、all、choice或sequence)加属性,用于定义一种非派生于特定类型的复杂类型。
1. 内容类型
所谓元素的内容是在标记之间的字符数据和子元素。复杂类型子元素的顺序和结构称为它的“内容模型”。复杂类型的内容类型有四种:简单类型、纯元素类型、混合类型和空类型。内容类型不依赖于属性,所有这些内容类型都允许有属性。
简单内容只允许有字符数据,它没有子元素。一般说来,简单类型和具有简单内容的复杂类型的唯一区别在于后者可以有属性。如下面的book元素。
<book isbn="0-764-58007-8">no.1</book>
要定义如上所示的book元素,必须按如下方法定义。复杂类型bookType的字符数据内容符合简单类型string,而且还添加了isbn属性。
<xsd:compleType name="bookType">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="isbn" type="xsd:token">
</xsd:extension>
</xsd:simpleConten>
</xsd:compleType>
纯元素内容只允许有子元素,而没有字符数据内容。下例显示了具有纯元素内容的bookinfo元素,它有两个子元素:title和author。
<bookinfo>
<title>三国演义</title>
<author >罗贯中</author>
</bookinfo>
可以按如下方式进行定义:
<xsd:compleType name="bookinfoType">
<xsd:sequence>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="author" type="xsd:string" />
</xsd:sequence>
</xsd:compleType>
混合内容既允许有字符数据又允许有子元素。经常用于字母和文档等形式自由的文本。下例中显示了具有混合内容的chapter元素。Chapter元素中直接饱含字符数据,以及子元素para。
<chapter >宴桃园豪杰三结义 斩黄巾英雄首立功
<para>话说天下大势,分久必合,合久必分。</para>
</chapter>
在创建所示的chapter元素类型定义时,需要设置complexType的mixed属性为true,允许有字符数据内容。mixed的默认值是false。
<xsd:compleType name="chapterType" mixed=true>
<xsd:sequence>
<xsd:element name="para" type="xsd:string" />
<!--..-->
</xsd:sequence>
</xsd:compleType>
空内容既不允许字符数据也不允许有子元素。带有空内容的元素通常在属性中具有值。有某些情况下,甚至可以没有属性,但空内容的元素存在本身就有意义。如XHTML中的<br/>元素就表示一个新行。
<price value="30" />
要定义空内容元素的类型时,只需指定属性,不用作其它内容的说明。
<xsd:compleType name="priceType">
<xsd:attribute name="value" type="xsd:integer">
</xsd:compleType>
2. 利用组合器控制结构
模式组允许把子元素声明或引用组合起来,从而构建更有意义的内容模型。模式组共有3种:all、choice、sequence。
sequence组合器,定义了一列元素并且必须按照模式中指定的顺序显示,所有子元素,如果出现的话,都必须按照该顺序出现。示例如下:
<xsd:element name="book">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="author" type="xsd:string" />
<xsd:element name="publisher" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
下面显示了一些book的有效实例。它的子元素都以正确的顺序出现:
<book>
<title>三国演义</title>
<author>罗贯中<author/>
<publisher>文艺出版社<publisher/>
</book>
choice组合器用来声明只有一个相容元素必须出现,用于互斥情况。语法如下:
<xsd:element name="book">
<xsd:complexType>
<xsd:choice>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="author" type="xsd:string"/>
<xsd:element name="publisher" type="xsd:string"/>
</xsd:choice>
</xsd:complexType>
</xsd:element>
下面的实例都是有效的,每一个实例包含一个符合choice组中声明的子元素。如果出现多个子元素,或者根本没有任何元素出现,那么该实例将是无效的。
<book>
<title>三国演义</title>
</book>
<book>
<author>罗贯中</author>
</book>
choice组还可以允许任意数量的子元素以任意顺序出现。这只要通过把choice组的maxOccurs属性设置为unbounded即可允许子元素以任何顺序出现,出现任意次数。另外,为了指定更加高级的内容,sequence和choice可以彼此嵌套,可以进行任意层数的嵌套。
<xsd:complexType name="tempType">
<xsd:sequence>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="author" type="xsd:string"/>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="amount" type="xsd:integer"/>
<xsd:element name="price" type="xsd:integer"/>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
all组合器来表示符合元素声明的所有元素都应该出现(以任何顺序)且只能出现一次。all组合器与choice和sequence不同:
· 只能包含元素声明和引用,而不能包含其它组。
· 不能出现多次。对于所包含的每个子元素,maxOccurs必须为1,而minOccurs只可以为0或1。不能出现在其它模式组中。all组必须在复杂类型的最高层。
语法如下:
<xsd:element name="book">
<xsd:complexType>
<xsd:all>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="author" type="xsd:string"/>
<xsd:element name="publisher" type="xsd:string" minOccurs="0"/>
<xsd:element name="price" type="xsd:integer" minOccurs="0"/>
</xsd:all>
</xsd:complexType>
</xsd:element>
根据上面的定义,下面显示了book的两个有效实例。price和publisher元素定义了minOccurs等于0,所以可以不出现。
<book>
<title>三国演义</title>
<author>罗贯中<author/>
<publisher>文艺出版社<publisher/>
<price>30</price>
</book>
<book>
<title>三国演义</title>
<author>罗贯中<author/>
</book>
Schema的名称空间
名称空间是XML Schema中的重要部分,它提供了一种避免元素命名冲突的方法。名称空间的用途在于为XML中使用的名称提供一个容器。
命名冲突
XML是可扩展的,而这种扩展是不受到控制。因为XML文档中使用的元素不是固定的,那么多个不同的XML文档使用同一个名字来描述不同类型的元素的情况就可能发生。而这种情况又往往会导致命名冲突。请看下面两个例子
如下XML文档在table元素中携带了水果的信息:
<table>
<tr>
<td>Apples</td>
<td>Bananas</td>
</tr>
</table>
如下XML文档在table元素中携带了桌子的信息:
<table>
<name>African Coffee Table</name>
<width>80</width>
<length>120</length>
</table>
如果上面两个XML文档片断碰巧在一起使用的话,那么将会出现命名冲突的情况。因为这两个片断都包含了<table>元素,而这两个table元素的定义与所包含的内容又各不相同。
使用前缀解决命名冲突问题
前面例子中所出现的命名冲突问题就是由于XML的可扩展性导致的,没有办法能够防止任何人以与其他人不兼容的方式对文档进行扩展。名称空间的出现就是为了解决这个问题。名称空间定义了一个标记的容器,为所有在文档使用的标记名称提供语境。实例文档中的元素类型或属性的名称可能直接与名称空间关联,或者就在属于某个名称空间。这样就可以避免命名的冲突。因此,可以这样改造上面的XML文档,在table元素前面加上前缀:
<h:table>
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
则家具table的信息变成:
<f:table>
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
现在已经没有元素命名冲突的问题了,因为这两个文档对各自的table元素使用了不同的前缀,table元素在两个文档中分别是(<h:table> 和<f:table>)。但是,前缀还不能彻底解决问题,因为任何人都可以创建前缀,如果两个文档创建了相同的前缀,则又有冲突问题。为了避免前缀冲突,可以使用如下的前缀声明:
f="http://www.example.org/html"
h="http://www.example.org/furniture"
将统一资源标识符(Uniform Resource Identifier,URI)跟前缀联系起来。因为URI跟标记或前缀不同,它具有唯一性的特点。这就是名称空间的解决方法。
使用名称空间
名称空间属性一般放置在元素的开始标记处,从属性xmlns开始,后面是前缀。其使用语法如下所示:
xmlns:namespace-prefix="namespace"
具体示例如下:
xmlns:f="http://www.w3.org/2000/10/XMLSchema"
前缀只是起着名称空间的代理的作用。名称空间的名称是URI,而不是前缀,在比较两个元素时,解析器是根据URI来识别它们的名称空间,而不是根据前缀。上面的例子则可以与不同的名称空间关联到一起:
<h:table xmlns:h=" http://www.example.org/html ">
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
下面是携带了家具table的信息XML文档:
<f:table xmlns:f=" http://www.example.org/furniture ">
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
URI、URL和URN
名称空间的声明将一个全局名称(URI)跟元素的名称联系在一起。URI只用作标识符。仅就作为XML名称空间来说,URI不必是有效的。也就是说,它不必指向任何位置。XML名称空间只将它们作为字符串处理。比较是逐个字符进行的,所以,下面的两个URI是不同的,虽然指向同一个文档。
http://www.example.org
http://example.org
而且名称空间是区分大小写的。即使仅有大小写区别,也会被解析为不同的URI。
大多数的URI都是URL或Internet地址。可能指向网络上的某个文件、用户电子邮箱(mailto:branch@example.org),或者是新闻组(news:comp.exmaple.xml)。
目前正在开发另一形式的URI,称为URN(Uniform Resource Name)。两者的区别在于:对地址来说,如果文档移动了位置,那么地址就失效了,而URN不是地址,它们与文档的位置无关。URN是用urn:前缀构建的,后跟一个前缀表明该URN的类型,最后是值。
urn:isbn: 0-764-58007-8
这里ISBN编号就是URN的实例。每个ISBN编号都代表了一本书。
名称空间的作用域
名称空间声明可以出现在文档中的任何开始标记中,它的作用域是它在其开始标记中出现的元素,以及其所有子元素等。
<bk:book xmlns:bk="http://www.example.org/2005/book">
<bk:title>三国演义</bk:title>
<bk:author >罗贯中</bk:author>
<bk:price >80.00 </bk:price>
<bk:recommendation>经典好书</bk:recommendation>
<ph:publish xmlns:ph="http://www.example.org/2005/publish">
<ph:publisher>文艺出版社</ph:publisher>
<ph:pubdate>1998.10</ph:pubdate>
</ph:publish>
</bk:book>
上面示例中,声明了二个名称空间,bk是顶层元素声明的,因此对所有元素都是有效的。ph是为publisher元素声明的,只对它的子元素有效。但是,如果还有一个book元素,它的子元素publisher使用了ph前缀,则是不合法的名称空间,因为超出了其作用域。一般来说,最好把所有名称空间声明都放到根元素的开始标记中。这样可以一下子看到文档的所有名称空间,对于它们的作用域也不会混淆。
在XSDL中使用的名称空间
1.目标名称空间
每一个XSDL模式文档都可以是一个名称空间,这称为它的目标名称空间(Target Namespace)。每个被全局声明所声明和定义的元素、属性、类型或组等都与该目标名称空间有关。下例中声明了http://www.example.org/2005/book的目标名称空间。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.example.org/2005/book"
targetNamespace="http://www.example.org/2005/book">
<xsd:element name="book" type="BookType"/>
<xsd:complexType name="BookType">
<xsd:sequence>
<xsd:element name="title" type="xs:string"/>
<xsd:element name="author" type="xs:string"/>
<xsd:element name="price" type="xs:integer"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
而下面的例子显示相应的元素如果如何出现在一个实例中。
<bk:book xmlns:bk="http://www.example.org/2005/book">
<bk:title>三国演义</bk:title>
<bk:author>罗贯中</bk:author>
<bk:price>80.00</bk:price>
</bk:book>
2. XML Schema名称空间
由于XSDL文件也是XML,所以名称空间也适用于它。在XSDL中使用的所有元素:schema、element和simpleType,都在XML Schema名称空间中,其名称空间名称为http://www.w3.org/2001/XMLSchema。此外,内置的简单类型也在这个名称空间中。最常映射到这个名称空间的前缀是xsd或xs。如上面所用到的例子,都采用xsd前缀。
3. XML Schema实例名称空间
XML Schema实例名称空间是实例中可以出现的四个与模式相关的属性的独立名称空间,其名称空间名称为http://www.w3.org/2001/XMLSchema-instance,一般映射到前缀xsi。当在实例中使用这四个属性:nil、type、 schemaLocation和noNamespaceSchemaLocation时,需要声明XML Schema实例名称空间,在实例中它们的名称必须加前缀。
4. 在模式文档中名称空间声明
为了解析模式文档的引用,模式文档必须包含目标名称空间和XML Schema名称空间的声明。可以把XML Schema名称空间映射到像xsd或xs这样的前缀,从而使目标名称空间成为默认名称空间。这样由XML Schema定义就显得非常清楚。如下例所示:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.example.org/2005/book"
targetNamespace="http://www.example.org/2005/book">
<xsd:element name="book" type="BookType"/>
<xsd:element name="title" type="xsd:string"/>
<xsd:complexType name="BookType">
<!--...-->
</xsd:complexType>
</xsd:schema>
另一种方法是把前缀映射到目标名称空间,并使XML Schema名称空间成为默认名称空间。下例显示了这一方法。声明中的名称本身不需要加前缀,因为它们自动成为XML Schema名称空间的一部分,唯一使用前缀的地方就是对其他组件的引用。如book元素通过限定名称引用类型BookType。如果不加前缀,那么解析器将在XML Schema名称空间中寻找BookType的定义。
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:bk="http://www.example.org/2005/book"
targetNamespace="http://www.example.org/2005/book">
<element name="book" type="bk:BookType"/>
<element name="title" type="xsd:string" />
<complexType name="BookType">
<!--...-->
</complexType>
</schema>
有时,还可以把前缀映射到所有名称空间也是可以的,这样整个文档也非常清晰。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:bk="http://www.example.org/2005/book"
targetNamespace="http://www.example.org/2005/book">
<xsd:element name="book" type="bk:BookType"/>
<xsd:element name="title" type="xsd:string" />
<xsd:complexType name=“BookType”>
<!--.. -->
</xsd:complexType>
</xsd:schema>
使用XML Spy建立Schema文档
利用前面有关的XML文档进行简化,现在使用XML Spy来建立Schema文档。
【例5.6】简化了的书籍XML文档,code5_6.xml。以此为例,下面逐步建立Schema文档。
<?xml version="1.0" encoding="gb2312" ?>
<book isbn="0-764-58007-8">
<title>三国演义</title>
<author >罗贯中</author>
<price >80.00 </price>
<resume>滚滚长江东逝水,浪花淘尽英雄。是非成败转头空:青山依旧在,几度夕阳红。白发渔樵江渚上,惯看秋月春风。一壶浊酒喜相逢:古今多少事,都付笑谈中。
</resume>
</book>
建立根节点
选择菜单File->New弹出Create new document 对话框,选择里面的xsd(XML Schema file),这样一个空的DTD文档就会被建立,然后从Schema/WSDL视图切换到Grid视图区,如图5.1所示。
图5.1新建一个Schema文档
窗体下方的几个按钮提供不同的视图来设计Schema文件。上例是选择是Grid视图,可以有许多详细信息。
接着,点击左上角的黑三角,编码方式项enconding默认值为UTF-8,将其改为GB2312,以便使用中文。在element处双击,添加根节点book。双击右侧Elements框里Append的xs:complexType,将其设置成复杂类型,紧接着选择Insert选项卡,设置为xs:sequence,完成后如图5.2所示:
图5.2添加一个元素,并设定元素类型
为根节点book添加子节点title,author,price,resume及属性isbn
在右边的Element控制面板上面,选择Add child->Element,为其增加四个子节点。并在Attribute控制面板上为每个子节点设置节点名和类型。同样利用Element面板和Attribute再次在ISBN添加属性并设置属性的类型,结果如图5.3所示:
图5.3添加子节点信息
这样Schema文档就建立好了,存盘为code5_6.xsd,切换到Text视图去,可以看到代码如下:
<?xml version="1.0" encoding="gb2312"?>
<!-- edited with XMLSPY v2004 rel. 2 U (http://www.xmlspy.com) by Jerry (jmu) -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="book">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
<xs:element name="price" type="xs:integer"/>
<xs:element name="resume" type="xs:string"/>
</xs:sequence>
<xs:attribute name="ISBN" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>
切换到Schema/WSDL视图区,可以看到上例的结构图,见图5.4。
图5.4定义好的书籍文档结构
为XML文档指定Schema
建立好了Schema文档,就需要用它来检验XML文档。XML Spy也可以为XML文档指定Schema文档。打开XML文档,选择DTD/Schema菜单下面的assign Schema…在出现有对话框中选择XML文档对应的Schema文档就可以了。那么,code5_6.xml中关于book节点的说明就变成:
<book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="c:\ch5\code5_6.xsd"
isbn="0-764-58007-8" >
为XML文档指定DTD后,可以对XML文档进行有效性检查。如同上一章一样,按F8按钮或按工具栏上带绿色√的图标按钮,进行XML文档有效性的检查。如果检查合格,则在文档下方会显示“This file is valid.”
另外,XML Spy也可以利用现有的XML文档直接生成Schema文档。打开XML文档后,选择DTD/Schema菜单下面的Generate DTD/Schema…,然后按照出现的对话框提示,一步一步也可以生成相应的Schema文档。
如同XML与DTD的关系一样,一般情况下,先规划好XML的文档结构,再设计Schema文档,最后根据Schema文档结构编辑XML文档。有了Schema文件,在XML Spy下建立XML文件,就只需在Grid视图区格架下填写元素和属性的值,很方便地建立XML文档。
小 结
Schema是描述XML文档的文档,用来定义XML文档的格式。最常用的用法之一就是依照预定义好的规则来XML验证文档的有效性。Schema可以确认:
· 元素和属性的结构;
· 元素出现的顺序;
· 对元素和属性的数据值的取值范围进行限定;
· 保证实例中值的唯一性。
同时,在Schema处理过程也可以扩充实例,为元素和属性插入默认和固定的值。并根据数据类型规范化空白空间。
与DTD相较而言,XML Schema采用XML语法,并且支持名称空间,提供丰富的数据类型定义手段,具有很强的描述能力、扩展能力和处理维护能力。
元素和属性是XML文档的基本构件。Schema主要包括元素和属性的声明和定义,模式文档由元素、属性、名称空间和XML文档中的其他节点构成的,并且至少要包含:schema根元素和XML模式名称空间的定义、元素定义。元素是利用element标识符来声明的,其中name属性是元素的名字,type属性是元素值的类型。属性实现的方法是使用attribute元素。对于元素或是属性,可以通过默认值和固定值的方式增加未出现的元素或者属性来扩充实例。默认值和固定值分别通过default和fixed属性设置。两个属性只能出现其中之一,它们是互斥的。
不管是元素或是属性,都要定义它的数据类型,XML Schema定义了两种主要的数据类型:预定义简单类型和复杂类型。简单类型包括原子类型、列表类型、联合类型合,新的简单类型可以通过从现有的简单类型引出定义。使用simpleType元素来定义和命名新的简单类型。属性只能定义为简单类型。复杂类型和简单类型之间最根本的区别就是:复杂类型的内容中可以包含其他元素或属性,由complexType元素创建。
名称空间是XML Schema中的重要部分,它提供了一种避免元素命名冲突的方法。名称空间的用途在于为XML中使用的名称提供一个容器。在XSDL中使用的名称空间主要有:目标名称空间,是每个模式文档的名称空间;XML Schema名称空间,其名称空间名称为http://www.w3.org/2001/XMLSchema;实例名称空间,其名称空间名称为http://www.w3.org/2001/XMLSchema-instance。