什么是Schema?
在计算机软件中,Schema这个词在不同的应用中有不同的含义,可以翻译为:架构、结构、规则、模式等。在XML中,Schema指的是定义和描述XML文档的规则,翻译为模式。
XML Schema与DTD的比较
我们看例4-3所示的XML文档。
例4-3 employee.xml
<?xml version="1.0" encoding="GB2312"?>
<employee sn="hr-01-001">
<name>张三</name>
<age>26</age>
<email>zhangsan@sunxin.org</email>
</employee>
如果采用DTD来定义例4-3的XML文档的结构,则一个可能的DTD如例4-4所示。
例4-4 employee.dtd
<!ELEMENT employee (name,age,email)>①
<!ELEMENT name (#PCDATA)>②
<!ELEMENT age (#PCDATA)>③
<!ELEMENT email (#PCDATA)>④
<!ATTLIST employee sn CDATA #required>⑤
这个DTD表示employee元素可以有三个子元素name、age和email,这三个子元素必须依次出现,它们的内容只能是字符数据。employee元素还有一个必需的属性sn,属性值为字符数据。
如果我们想要在上述DTD定义的基础上进一步限制元素的内容,例如,限制age元素的内容只能是正整数,或者限制email元素的内容必须是有效的邮件格式,DTD就无能为力了。下面我们采用XML Schema来定义例4-3的XML文档的结构并约束文档的内容,如例4-5所示。
例4-5 employee.xsd
<?xml version="1.0" encoding="GB2312"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="employee" type="empType"/>①
<xs:complexType name="empType">②
<xs:sequence>③
<xs:element name="name" type="xs:string"/>④
<xs:element name="age" type="xs:positiveInteger"/>⑤
<xs:element name="email">⑥
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern ⑦
value="[a-z0-9A-Z]+([-|\.]?[a-z0-9A-Z])*@([a-z0-9A-Z]+
(-[a-z0-9A-Z]+)?\.)+ [a-zA-Z]{2,}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<xs:attribute name="sn" type="xs:string" use="required"/>⑧
</xs:complexType>
</xs:schema>
① 使用XSDL中的xs:element元素来声明employee元素,它的类型为empType,这是自定义的类型,在②处定义。
② 使用XSDL中的xs:complexType元素来定义复杂类型,属性name指定自定义类型的名字,该名字可以被xs:element元素的type属性所引用。
③ XSDL中的xs:sequence元素用于指定在它内部声明的元素必须按照声明的顺序出现。①、②、③合起来与例4-4中的①(<!ELEMENT employee (name,email)>)对应。
④ 声明name元素,其内容只能是字符串值。与例4-4中的②(<!ELEMENT name (#PCDATA)>)对应。
⑤ 声明age元素,其内容只能是正整数。与例4-4中的③(<!ELEMENT age (#PCDATA)>)对应。
⑥ 声明email元素,使用正则表达式对email元素的内容做了限定,要求内容必须符合邮件格式的规范。与例4-4中的④(<!ELEMENT email (#PCDATA)>)对应。
⑦ XSDL中的xs:pattern元素使用正则表达式来限制值的范围。
⑧ 声明sn属性,属性值是字符串,该属性是必需的(use="required")。与例4-4中的⑤(<!ATTLIST employee sn CDATA #required>)对应。
从例4-5的XML Schema的定义可以看出,XML Schema具有丰富的数据类型,除了内置的数据类型外(例如:xs:string、xs:positiveInteger),我们还可以定义自己的数据类型(例如:empType)。
如果以建筑图纸与建筑的关系为例来比较XML Schema和DTD,那么DTD只能定义厨房、阳台的位置,主卧的结构等,而XML Schema还可以定义卧室使用什么类型的地板、阳台选用什么类型的铝合窗,即XML Schema定义了丰富的数据类型。
DTD不能很好地满足XML自动化处理的要求(由于采用了非XML语法格式),缺乏对文档结构、元素、属性、数据类型等约束的足够描述。与XML Schema相比,DTD具有以下的局限性:
(2)约束定义能力不足,无法对XML实例文档做出更细致的语义限制;
(3)DTD定义不够结构化,重用的代价相对较高;
(4)不使用XML语法,无法采用一致的方式来处理XML文档和DTD。
术语明晰
XML Schema中有一些术语不是很好理解,为了便于后续内容的学习,这一节我们首先明确一些术语的含义。
模式
模式指的是依照XML Schema规范,使用http://www.w3.org/2001/XMLSchema名称空间中的元素来描述XML文档结构的规则集。模式由许多不同类型的组件构成,包括:元素、属性、简单类型、复杂类型、记号(notation)、元素组(模型组定义)、属性组和标识约束等。
模式在模式文档中定义,模式文档通常以文件的形式存在,扩展名为.xsd。不过,模式文档不是必须要以文件的形式存在,它们在应用程序中可以以字节流的形式存在,也可以作为数据库记录而存在。
每个模式定义都以一个根元素xs:schema开始,该元素属于http://www.w3.org/2001/XMLSchema名称空间。
实例和模式
模式用于定义XML文档的结构,并对文档的内容进行约束,符合某个模式的XML文档称为实例。检查一个实例文档是否符合一个或多个模式的过程,称为模式验证(schema validation)。包含模式定义的文档称为模式文档,符合某个特定模式的XML文档称为实例文档。
声明和定义
在XML Schema推荐标准中,使用了"声明"和"定义"两个术语。对于出现在实例文档中并通过名称来验证的组件使用"声明",这些组件包括元素、属性、记号(notation),如元素声明、属性声明。对于在模式内部中的组件使用"定义",这些组件包括数据类型、元素组、属性组和一致性约束,如数据类型定义,元素组定义。
元素和属性的声明
元素和属性分别通过http://www.w3.org/2001/XMLSchema名称空间中的element和attribute元素来声明,如例4-6所示。
例4-6 在模式文档中声明元素和属性
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="student" type="xs:string"/>
<xs:attribute name="sn" type="xs:integer"/>
</xs:schema>
元素和属性的类型
我们看例4-7的XML文档。
例4-7 desk.xml
<?xml version="1.0" encoding="GB2312"?>
<desk length="1.2" width="0.6" color="white">
<name>办公桌</name>
<drawer>
<amount>2</amount>
</drawer>
</desk>
从本质上来说,XML文档只能保存文本格式的数据,例如,length属性的值1.2和amount元素的内容2,都是字符形式,然而,为了让XML文档能够进行数据交换(如与数据库或程序内存中的数据进行交换),需要人为地为XML文档中的数据定义一些数据类型,以方便对元素内容和属性值进行验证。DTD对数据类型的支持是非常有限的,而XML Schema则提供了丰富的数据类型,并且允许自定义数据类型。
在XML Schema中,元素和属性都有数据类型,元素的数据类型即元素内容的数据类型,属性的数据类型即属性值的数据类型。XML Schema把元素和属性的概念与其数据类型分开,这就允许不同名称的元素拥有相同类型的数据。例如,我们可以声明两个元素homeAddress和officeAddress,它们具有相同的结构,但名称不同,我们只需要定义一种数据类型addressType,然后在两个元素的声明中引用它,如例4-8所示。
例4-8 元素homeAddress和officeAddress具有相同的类型
<xs:element name="homeAddress" type="addressType"/>
<xs:element name="officeAddress" type="addressType"/>
<xs:complexType name="addressType">
<xs:sequence>
<xs:element name="street" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
元素如果包含子元素或者带有属性则称为具有复杂类型,如例4-7中的desk和drawer元素;反之元素如果仅仅包含字符数据(可以是数字、字符串、日期或者其他数据类型),不包含任何子元素,也不带有属性则称为具有简单类型,如例4-7中的name和amount元素。属性总是具有简单类型,因为属性不能有子元素或其他属性。
简单类型
具有简单类型的元素只能包含字符数据,不能包含子元素,也不能有属性。XML Schema推荐标准内置了44种简单类型,如例4-6、例4-8中的xs:string、xs:integer等,这些简单类型可以直接引用。XML Schema内置的数据类型的层次结构如图 4-1所示。
XML Schema支持类型的派生,这和面向对象语言中对象的继承有些类似,但又不完全相同。XML Schema类型的派生分为限制(restriction)和扩展(extension),通过限制派生的新类型的值范围是原类型值范围的子集,通过扩展则可以为现有类型添加新的元素和属性。
对于简单类型,只有限制派生而没有扩展派生,我们只能通过限制一个现有的简单类型(内置的简单类型或派生于内置简单类型的简单类型)来派生一个新的简单类型。那如何对现有的简单类型进行限制呢?XML Schema给我们提供了12个面(facet),用于指定一个值的有效范围、约束值的长度和精度、枚举一系列的有效值,或者指定有效值必须匹配的正则表达式。
图4-1 内置数据类型的层次结构
面与类型的关系就好像棱镜的面与棱镜的关系,透过不同的面,你看到的景观(内容)是不同的。在XML Schema中,面相当于是对一个现有的简单类型添加某些约束(限制),约束不同,得到的结果就不同。也就是说,你可以把面(facet)看成是对数据类型的约束,目的是为了让它的值限制在一定的范围内。实际上,这些面就是http://www.w3.org/2001/XMLSchema名称空间中的一些元素,只不过这些元素是用来对现有类型的值进行约束的,因而称为面。
要定义新的简单类型,使用xs:simpleType元素,要对现有的基类型进行限制,使用xs:restriction元素,并在该元素的内部使用限制值范围的面(facet)。例如,我们定义一个雇员年龄的类型,雇员年龄要求在18~60岁之间,我们可以从内置的xs:integer类型派生一个新的简单类型ageType,然后使用xs:minInclusive和xs:maxInclusive面来限制年龄值的范围,如例4-9所示。
例4-9 使用minInclusive和maxInclusive面来限制年龄值的范围
<xs:simpleType name="ageType">
<xs:restriction base="xs:integer">①
<xs:minInclusive value="18"/>
<xs:maxInclusive value="60"/>
</xs:restriction>
</xs:simpleType>
① xs:restriction元素的base属性指定要限制的基类型
复杂类型
具有复杂类型的元素可以有子元素和属性。复杂类型使用xs:complexType元素来定义。
复杂类型要么具有简单内容,要么具有复杂内容。元素的"内容"是指在它的开始标签和结束标签之间的字符数据和子元素。简单内容指的是它只包含字符数据内容,而没有子元素(可以有属性),简单内容使用xs:simpleContent元素来定义;除此之外的元素内容称为复杂内容,复杂内容使用xs:complexContent元素来定义。
例如,对于例4-10所示的具有简单内容的book元素,它的复杂类型定义如例4-11所示。
例4-10 实例文档中的book元素
<book isbn= "978-7-121-06812-6">Struts 2深入详解</book>
例4-11 模式文档中的bookType类型定义
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="isbn" type="xs:token"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
对于例4-12所示的具有复杂内容的student元素,它的复杂类型定义如例4-13所示。
例4-12 实例文档中的student元素
<student>
<name>张三</name>
<age>20</age>
</student>
例4-13 模式文档中的studentType类型定义
<xs:element name="book" type="studentType"/>
<xs:complexType name="studentType">
<xs:complexContent>
<xs:restriction base="xs:anyType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:integer"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
XMLSchema 模式与命名空间
模式与名称空间
DTD的问题就在于和名称空间不相关,当你为一个XML文档使用了名称空间后,文档的DTD就需要重写。XML Schema对名称空间提供了很好的支持。在编写模式文档时,你可以指定该模式文档是为哪一个名称空间声明和定义组件,这个名称空间称为目标名称空间。
4.5.6.1 目标名称空间
目标名称空间使用xs:schema元素的targetNamespace属性来指定,如例4-16所示。
例4-16 book3.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.sunxin.org/book"①
xmlns:book="http://www.sunxin.org/book">②
<xs:element name="book" type="book:bookType"/>③
<xs:complexType name="bookType">④
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
① 我们在xs:schema元素上使用targetNamespace属性指定目标名称空间为:http://www.sunxin.org/book,即指明了在该模式文档中声明的元素、定义的类型都属于http://www.sunxin.org/book名称空间。
② 声明名称空间http://www.sunxin.org/book,并为该名称空间绑定前缀book,使用该前缀来引用http://www.sunxin.org/book名称空间中的类型。
③ 由于指定了目标名称空间,文档中定义的类型都属于目标名称空间,因此在引用类型时,需要加上book前缀。
④ 在指定类型名时,不需要加上任何前缀,当使用了targetNamespace属性后,在模式文档中定义的任何类型都属于目标名称空间。
在模式文档中,我们很容易就能区分出不同名称空间中的元素和类型,带有xs前缀的元素和类型属于http://www.w3.org/2001/XMLSchema名称空间,而其他的元素和类型则属于目标名称空间。需要注意的是,只有模式文档中的全局元素和全局属性才属于目标名称空间,在本例中,声明的全局元素name属于目标名称空间,而局部元素title和author则不属于目标名称空间。
为了简化模式文档对目标名称空间中的元素和类型的引用,我们可以利用默认名称空间来简化引用,例4-16如果使用了默认名称空间,可以修改为如例4-17所示。
例4-17 使用了默认名称空间的book4.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.sunxin.org/book"
xmlns="http://www.sunxin.org/book">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
我们在xs:schema元素上声明了默认名称空间http://www.sunxin.org/book,因此在声明book元素时,引用bookType类型就无须再添加前缀了。
符合上述模式文档的XML实例文档如例4-18所示。
例4-18 book.xml
<?xml version="1.0" encoding="GB2312"?>
<book:book xmlns:book="http://www.sunxin.org/book" isbn="978-7-121-06812-6">
<title>《Struts 2深入详解》</title>
<author>孙鑫</author>
</book:book>
在例4-17的模式文档中,在bookType类型定义中声明的元素title和author是局部元素,没有被目标名称空间所限定,因此在实例文档中,对title和author元素不要添加book前缀。局部元素和属性的限定
在例4-17中,局部元素title和author没有被目标名称空间所限定,如果要限定局部元素和属性,可以通过xs:schema元素的elementFormDefault和attibuteFormDefault属性来设置。
为了指定模式文档中局部声明的元素必须被限定,可以将xs:schema元素的elementFormDefault属性的值设为"qualified"。
将例4-17中的xs:schema元素修改为如例4-19所示。
例4-19 book5.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.sunxin.org/book"
elementFormDefault="qualified">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
elementFormDefault属性的默认值是unqualified,即对局部声明的元素不加限定。
我们修改了模式文档,相应的就要修改实例文档,如例4-20所示。
例4-20 book2.xml
<?xml version="1.0" encoding="GB2312"?>
<book:book xmlns:book="http://www.sunxin.org/book" isbn="978-7-121-06812-6">
<book:title>《Struts 2深入详解》</book:title>
<book:author>孙鑫</book:author>
</book:book>
在例4-20的实例文档中,所有的元素都属于同一个名称空间,因此我们可以声明一个默认的名称空间,来省略前缀的使用,如例4-21所示。
例4-21 book3.xml
<?xml version="1.0" encoding="GB2312"?>
<book xmlns="http://www.sunxin.org/book" isbn="978-7-121-06812-6">
<title>《Struts 2深入详解》</title>
<author>孙鑫</author>
</book>
属性的限定和元素的限定是类似的。如果属性被声明为全局属性或者xs:schema元素的attibuteFormDefault属性被设置成"qualified"的话,那么属性就必须被限定,在实例文档中将以带名称空间前缀的方式出现。实际上,需要限定的属性必须明确的加上名称空间前缀,因为XML名称空间推荐标准中并没有给出关于属性的默认名称空间的机制,一个没有前缀的属性将不在任何的名称空间中。属性是附属于元素的,对属性添加名称空间不是很有必要,因此在多数应用中都没有给属性添加限定。
使用了attibuteFormDefault属性的模式文档,如例4-22所示。
例4-22 book6.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.sunxin.org/book"
targetNamespace="http://www.sunxin.org/book"
elementFormDefault="qualified"
attributeFormDefault="qualified">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
attributeFormDefault属性的默认值是unqualified,即对局部声明的属性不加限定。
一个符合模式(例4-22)的实例文档如例4-23所示。
例4-23 book4.xml
<?xml version="1.0" encoding="GB2312"?>
<book:book xmlns:book="http://www.sunxin.org/book" book:isbn="978-7-121-06812-6">
<book:title>《Struts 2深入详解》</book:title>
<book:author>孙鑫</book:author>
</book:book>
除了使用xs:schema元素的elementFormDefault和attibuteFormDefault属性来限定局部元素和局部属性外,我们还可以在xs:element和xs:attribute元素上分别使用form属性来限定局部元素和局部属性。我们看例4-24。
例4-24 book7.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.sunxin.org/book">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string" form="qualified"/>
<xs:element name="author" type="xs:string" form="qualified"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token" form="qualified"/>
</xs:complexType>
</xs:schema>
例4-24所示的模式文档和例4-22所示的模式文档的效果是一样的。
要注意的是,在xs:schema元素上使用elementFormDefault和attibuteFormDefault属性,影响的是整个模式文档中的局部元素和局部属性,而在xs:element和xs:attribute元素上使用form属性,影响的是当前的局部元素和局部属性。
未声明的目标名称空间
对于很多不使用名称空间的XML文档,你需要使用没有声明目标名称空间的模式文档。我们看例4-25。
例4-25 book8.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
在这个模式文档中我们没有声明目标名称空间,因此定义的bookType类型和声明的book元素都是没有名称空间限定的。在引用类型或元素时,不需要添加任何的名称空间前缀。在本例中,声明book元素时,type属性直接引用了bookType类型。与之相对的是,例4-25的模式文档中使用的所有XML Schema元素和类型都是通过与XML Schema名称空间相关联的名称空间前缀"xs"来明确进行限定的。
一个符合模式(例4-25)的实例文档如例4-26所示。
例4-26 book5.xml
<?xml version="1.0" encoding="GB2312"?>
<book isbn="978-7-121-06812-6">
<title>《Struts 2深入详解》</title>
<author>孙鑫</author>
</book>
要注意的是,在没有声明目标名称空间的模式文档中,强烈建议对所有的XML Schema元素和类型使用一个和XML Schema名称空间相关联的名称空间前缀(如xs或xsd)来明确实施限定。如果你对XML Schema的元素和类型使用了默认名称空间,那么对XML Schema类型的引用也许不能和对用户自定义类型的引用相区分,从而导致模式文档出现错误。例4-27错误的使用了默认名称空间,使得对自定义类型的引用和对XML Schema类型的引用不能区分。
例4-27 book9.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<element name="book" type="bookType"/>
<complexType name="bookType">
<sequence>
<element name="title" type="string"/>
<element name="author" type="string"/>
</sequence>
<attribute name="isbn" type="token"/>
</complexType>
</schema>
'bookType' must refer to an existing simple or complex type.
这是因为模式验证器认为对bookType类型的引用(没有前缀的引用),引用的是默认名称空间(即http://www.w3.org/2001/XMLSchema名称空间)中的类型,而XML Schema名称空间中并没有bookType这种类型,因此就报出了错误。
xsi:schemaLocation详解
在实例中引用模式文档
XML Schema提供了两个在实例文档中使用的特殊属性,用于指出模式文档的位置。这两个属性是:xsi:schemaLocation和xsi:noNamespaceSchemaLocation,前者用于声明了目标名称空间的模式文档,后者用于没有目标名称空间的模式文档,它们通常在实例文档中使用。
4.5.7.1 xsi:schemaLocation属性
xsi:schemaLocation属性的值由一个URI引用对组成,两个URI之间以空白符分隔。第一个URI是名称空间的名字,第二个URI给出模式文档的位置,模式处理器将从这个位置读取模式文档,该模式文档的目标名称空间必须与第一个URI相匹配。我们看例4-28。
例4-28 book6.xml
<?xml version="1.0" encoding="GB2312"?>
<book xmlns="http://www.sunxin.org/book" ①
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ②
xsi:schemaLocation="http://www.sunxin.org/book http://www.sunxin.org/
book.xsd">③
<title>《Struts 2深入详解》</title>
<author>孙鑫</author>
</book>
① 声明默认的名称空间(http://www.sunxin.org/book)。
② 声明XML Schema实例名称空间(http://www.w3.org/2001/XMLSchema-instance),并将xsi前缀与该名称空间绑定,这样模式处理器就可以识别xsi:schemaLocation属性。XML Schema实例名称空间的前缀通常使用xsi。
③ 使用xsi:schemaLocation属性指定名称空间http://www.sunxin.org/book和模式位置http://www.sunxin.org/book.xsd相关。要注意,在这个例子中,book.xsd中声明的目标名称空间要求是http://www.sunxin.org/book。
一个可能的模式文档book.xsd如例4-29所示。
例4-29 book.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.sunxin.org/book"
elementFormDefault="qualified">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
实际上,xsi:schemaLocation属性的值也可以由多个URI引用对组成,每个URI引用对之间使用空白符分隔。例4-30的实例文档使用了多个名称空间,xsi:schemaLocation属性的值包含了两对URI。
例4-30 books.xml
<?xml version="1.0" encoding="GB2312"?>
<books xmlns="http://www.sunxin.org/bks" xmlns:p="http://www.sunxin.org/people"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sunxin.org/bks bks.xsd
http://www.sunxin.org/people people.xsd">
<book>
<title>JSP深入编程</title>
<author>
<p:name>张三</p:name>
<p:title>作家</p:title>
</author>
</book>
<book>
<title>XML从入门到精通</title>
<author>
<p:name>李四</p:name>
<p:title>教师</p:title>
</author>
</book>
</books>
XML Schema推荐标准中指出,xsi:schemaLocation属性可以在实例中的任何元素上使用,而不一定是根元素,不过,xsi:schemaLocation属性必须出现在它要验证的任何元素和属性之前。
此外,要注意的是,XML Schema推荐标准并没有要求模式处理器必须要使用xsi:schemaLocation属性,某些模式处理器可以通过其他的方式来得到模式文档的位置,而忽略xsi:schemaLocation属性。
xsi:noNamespaceSchemaLocation属性
xsi:noNamespaceSchemaLocation属性用于引用没有目标名称空间的模式文档。与xsi:schemaLocation属性不同的是,xsi:noNamespaceSchemaLocation属性的值是单一的值,只是用于指定模式文档的位置。例4-31显示了在实例文档中xsi:noNamespaceSchema Location属性的使用。
例4-31 book7.xml
<?xml version="1.0" encoding="GB2312"?>
<book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="book.xsd"
isbn="978-7-121-06812-6" >
<title>《Struts 2深入详解》</title>
<author>孙鑫</author>
</book>
与xsi:schemaLocation属性一样,xsi:noNamespaceSchemaLocation属性也可以在实例中的任何元素上使用,而不一定是根元素,不过,xsi:noNamespaceSchemaLocation属性必须出现在它要验证的任何元素和属性之前。
此外,要注意的是,XML Schema推荐标准并没有要求模式处理器必须要使用xsi:noNamespaceSchemaLocation属性,某些模式处理器可以通过其他的方式来得到模式文档的位置,而忽略xsi:noNamespaceSchemaLocation属性。
什么是Schema?
在计算机软件中,Schema这个词在不同的应用中有不同的含义,可以翻译为:架构、结构、规则、模式等。在XML中,Schema指的是定义和描述XML文档的规则,翻译为模式。
XML Schema与DTD的比较
我们看例4-3所示的XML文档。
例4-3 employee.xml
<?xml version="1.0" encoding="GB2312"?>
<employee sn="hr-01-001">
<name>张三</name>
<age>26</age>
<email>zhangsan@sunxin.org</email>
</employee>
如果采用DTD来定义例4-3的XML文档的结构,则一个可能的DTD如例4-4所示。
例4-4 employee.dtd
<!ELEMENT employee (name,age,email)>①
<!ELEMENT name (#PCDATA)>②
<!ELEMENT age (#PCDATA)>③
<!ELEMENT email (#PCDATA)>④
<!ATTLIST employee sn CDATA #required>⑤
这个DTD表示employee元素可以有三个子元素name、age和email,这三个子元素必须依次出现,它们的内容只能是字符数据。employee元素还有一个必需的属性sn,属性值为字符数据。
如果我们想要在上述DTD定义的基础上进一步限制元素的内容,例如,限制age元素的内容只能是正整数,或者限制email元素的内容必须是有效的邮件格式,DTD就无能为力了。下面我们采用XML Schema来定义例4-3的XML文档的结构并约束文档的内容,如例4-5所示。
例4-5 employee.xsd
<?xml version="1.0" encoding="GB2312"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="employee" type="empType"/>①
<xs:complexType name="empType">②
<xs:sequence>③
<xs:element name="name" type="xs:string"/>④
<xs:element name="age" type="xs:positiveInteger"/>⑤
<xs:element name="email">⑥
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern ⑦
value="[a-z0-9A-Z]+([-|\.]?[a-z0-9A-Z])*@([a-z0-9A-Z]+
(-[a-z0-9A-Z]+)?\.)+ [a-zA-Z]{2,}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<xs:attribute name="sn" type="xs:string" use="required"/>⑧
</xs:complexType>
</xs:schema>
① 使用XSDL中的xs:element元素来声明employee元素,它的类型为empType,这是自定义的类型,在②处定义。
② 使用XSDL中的xs:complexType元素来定义复杂类型,属性name指定自定义类型的名字,该名字可以被xs:element元素的type属性所引用。
③ XSDL中的xs:sequence元素用于指定在它内部声明的元素必须按照声明的顺序出现。①、②、③合起来与例4-4中的①(<!ELEMENT employee (name,email)>)对应。
④ 声明name元素,其内容只能是字符串值。与例4-4中的②(<!ELEMENT name (#PCDATA)>)对应。
⑤ 声明age元素,其内容只能是正整数。与例4-4中的③(<!ELEMENT age (#PCDATA)>)对应。
⑥ 声明email元素,使用正则表达式对email元素的内容做了限定,要求内容必须符合邮件格式的规范。与例4-4中的④(<!ELEMENT email (#PCDATA)>)对应。
⑦ XSDL中的xs:pattern元素使用正则表达式来限制值的范围。
⑧ 声明sn属性,属性值是字符串,该属性是必需的(use="required")。与例4-4中的⑤(<!ATTLIST employee sn CDATA #required>)对应。
从例4-5的XML Schema的定义可以看出,XML Schema具有丰富的数据类型,除了内置的数据类型外(例如:xs:string、xs:positiveInteger),我们还可以定义自己的数据类型(例如:empType)。
如果以建筑图纸与建筑的关系为例来比较XML Schema和DTD,那么DTD只能定义厨房、阳台的位置,主卧的结构等,而XML Schema还可以定义卧室使用什么类型的地板、阳台选用什么类型的铝合窗,即XML Schema定义了丰富的数据类型。
DTD不能很好地满足XML自动化处理的要求(由于采用了非XML语法格式),缺乏对文档结构、元素、属性、数据类型等约束的足够描述。与XML Schema相比,DTD具有以下的局限性:
(2)约束定义能力不足,无法对XML实例文档做出更细致的语义限制;
(3)DTD定义不够结构化,重用的代价相对较高;
(4)不使用XML语法,无法采用一致的方式来处理XML文档和DTD。
术语明晰
XML Schema中有一些术语不是很好理解,为了便于后续内容的学习,这一节我们首先明确一些术语的含义。
模式
模式指的是依照XML Schema规范,使用http://www.w3.org/2001/XMLSchema名称空间中的元素来描述XML文档结构的规则集。模式由许多不同类型的组件构成,包括:元素、属性、简单类型、复杂类型、记号(notation)、元素组(模型组定义)、属性组和标识约束等。
模式在模式文档中定义,模式文档通常以文件的形式存在,扩展名为.xsd。不过,模式文档不是必须要以文件的形式存在,它们在应用程序中可以以字节流的形式存在,也可以作为数据库记录而存在。
每个模式定义都以一个根元素xs:schema开始,该元素属于http://www.w3.org/2001/XMLSchema名称空间。
实例和模式
模式用于定义XML文档的结构,并对文档的内容进行约束,符合某个模式的XML文档称为实例。检查一个实例文档是否符合一个或多个模式的过程,称为模式验证(schema validation)。包含模式定义的文档称为模式文档,符合某个特定模式的XML文档称为实例文档。
声明和定义
在XML Schema推荐标准中,使用了"声明"和"定义"两个术语。对于出现在实例文档中并通过名称来验证的组件使用"声明",这些组件包括元素、属性、记号(notation),如元素声明、属性声明。对于在模式内部中的组件使用"定义",这些组件包括数据类型、元素组、属性组和一致性约束,如数据类型定义,元素组定义。
元素和属性的声明
元素和属性分别通过http://www.w3.org/2001/XMLSchema名称空间中的element和attribute元素来声明,如例4-6所示。
例4-6 在模式文档中声明元素和属性
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="student" type="xs:string"/>
<xs:attribute name="sn" type="xs:integer"/>
</xs:schema>
元素和属性的类型
我们看例4-7的XML文档。
例4-7 desk.xml
<?xml version="1.0" encoding="GB2312"?>
<desk length="1.2" width="0.6" color="white">
<name>办公桌</name>
<drawer>
<amount>2</amount>
</drawer>
</desk>
从本质上来说,XML文档只能保存文本格式的数据,例如,length属性的值1.2和amount元素的内容2,都是字符形式,然而,为了让XML文档能够进行数据交换(如与数据库或程序内存中的数据进行交换),需要人为地为XML文档中的数据定义一些数据类型,以方便对元素内容和属性值进行验证。DTD对数据类型的支持是非常有限的,而XML Schema则提供了丰富的数据类型,并且允许自定义数据类型。
在XML Schema中,元素和属性都有数据类型,元素的数据类型即元素内容的数据类型,属性的数据类型即属性值的数据类型。XML Schema把元素和属性的概念与其数据类型分开,这就允许不同名称的元素拥有相同类型的数据。例如,我们可以声明两个元素homeAddress和officeAddress,它们具有相同的结构,但名称不同,我们只需要定义一种数据类型addressType,然后在两个元素的声明中引用它,如例4-8所示。
例4-8 元素homeAddress和officeAddress具有相同的类型
<xs:element name="homeAddress" type="addressType"/>
<xs:element name="officeAddress" type="addressType"/>
<xs:complexType name="addressType">
<xs:sequence>
<xs:element name="street" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
元素如果包含子元素或者带有属性则称为具有复杂类型,如例4-7中的desk和drawer元素;反之元素如果仅仅包含字符数据(可以是数字、字符串、日期或者其他数据类型),不包含任何子元素,也不带有属性则称为具有简单类型,如例4-7中的name和amount元素。属性总是具有简单类型,因为属性不能有子元素或其他属性。
简单类型
具有简单类型的元素只能包含字符数据,不能包含子元素,也不能有属性。XML Schema推荐标准内置了44种简单类型,如例4-6、例4-8中的xs:string、xs:integer等,这些简单类型可以直接引用。XML Schema内置的数据类型的层次结构如图 4-1所示。
XML Schema支持类型的派生,这和面向对象语言中对象的继承有些类似,但又不完全相同。XML Schema类型的派生分为限制(restriction)和扩展(extension),通过限制派生的新类型的值范围是原类型值范围的子集,通过扩展则可以为现有类型添加新的元素和属性。
对于简单类型,只有限制派生而没有扩展派生,我们只能通过限制一个现有的简单类型(内置的简单类型或派生于内置简单类型的简单类型)来派生一个新的简单类型。那如何对现有的简单类型进行限制呢?XML Schema给我们提供了12个面(facet),用于指定一个值的有效范围、约束值的长度和精度、枚举一系列的有效值,或者指定有效值必须匹配的正则表达式。
图4-1 内置数据类型的层次结构
面与类型的关系就好像棱镜的面与棱镜的关系,透过不同的面,你看到的景观(内容)是不同的。在XML Schema中,面相当于是对一个现有的简单类型添加某些约束(限制),约束不同,得到的结果就不同。也就是说,你可以把面(facet)看成是对数据类型的约束,目的是为了让它的值限制在一定的范围内。实际上,这些面就是http://www.w3.org/2001/XMLSchema名称空间中的一些元素,只不过这些元素是用来对现有类型的值进行约束的,因而称为面。
要定义新的简单类型,使用xs:simpleType元素,要对现有的基类型进行限制,使用xs:restriction元素,并在该元素的内部使用限制值范围的面(facet)。例如,我们定义一个雇员年龄的类型,雇员年龄要求在18~60岁之间,我们可以从内置的xs:integer类型派生一个新的简单类型ageType,然后使用xs:minInclusive和xs:maxInclusive面来限制年龄值的范围,如例4-9所示。
例4-9 使用minInclusive和maxInclusive面来限制年龄值的范围
<xs:simpleType name="ageType">
<xs:restriction base="xs:integer">①
<xs:minInclusive value="18"/>
<xs:maxInclusive value="60"/>
</xs:restriction>
</xs:simpleType>
① xs:restriction元素的base属性指定要限制的基类型
复杂类型
具有复杂类型的元素可以有子元素和属性。复杂类型使用xs:complexType元素来定义。
复杂类型要么具有简单内容,要么具有复杂内容。元素的"内容"是指在它的开始标签和结束标签之间的字符数据和子元素。简单内容指的是它只包含字符数据内容,而没有子元素(可以有属性),简单内容使用xs:simpleContent元素来定义;除此之外的元素内容称为复杂内容,复杂内容使用xs:complexContent元素来定义。
例如,对于例4-10所示的具有简单内容的book元素,它的复杂类型定义如例4-11所示。
例4-10 实例文档中的book元素
<book isbn= "978-7-121-06812-6">Struts 2深入详解</book>
例4-11 模式文档中的bookType类型定义
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="isbn" type="xs:token"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
对于例4-12所示的具有复杂内容的student元素,它的复杂类型定义如例4-13所示。
例4-12 实例文档中的student元素
<student>
<name>张三</name>
<age>20</age>
</student>
例4-13 模式文档中的studentType类型定义
<xs:element name="book" type="studentType"/>
<xs:complexType name="studentType">
<xs:complexContent>
<xs:restriction base="xs:anyType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:integer"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
XMLSchema 模式与命名空间
模式与名称空间
DTD的问题就在于和名称空间不相关,当你为一个XML文档使用了名称空间后,文档的DTD就需要重写。XML Schema对名称空间提供了很好的支持。在编写模式文档时,你可以指定该模式文档是为哪一个名称空间声明和定义组件,这个名称空间称为目标名称空间。
4.5.6.1 目标名称空间
目标名称空间使用xs:schema元素的targetNamespace属性来指定,如例4-16所示。
例4-16 book3.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.sunxin.org/book"①
xmlns:book="http://www.sunxin.org/book">②
<xs:element name="book" type="book:bookType"/>③
<xs:complexType name="bookType">④
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
① 我们在xs:schema元素上使用targetNamespace属性指定目标名称空间为:http://www.sunxin.org/book,即指明了在该模式文档中声明的元素、定义的类型都属于http://www.sunxin.org/book名称空间。
② 声明名称空间http://www.sunxin.org/book,并为该名称空间绑定前缀book,使用该前缀来引用http://www.sunxin.org/book名称空间中的类型。
③ 由于指定了目标名称空间,文档中定义的类型都属于目标名称空间,因此在引用类型时,需要加上book前缀。
④ 在指定类型名时,不需要加上任何前缀,当使用了targetNamespace属性后,在模式文档中定义的任何类型都属于目标名称空间。
在模式文档中,我们很容易就能区分出不同名称空间中的元素和类型,带有xs前缀的元素和类型属于http://www.w3.org/2001/XMLSchema名称空间,而其他的元素和类型则属于目标名称空间。需要注意的是,只有模式文档中的全局元素和全局属性才属于目标名称空间,在本例中,声明的全局元素name属于目标名称空间,而局部元素title和author则不属于目标名称空间。
为了简化模式文档对目标名称空间中的元素和类型的引用,我们可以利用默认名称空间来简化引用,例4-16如果使用了默认名称空间,可以修改为如例4-17所示。
例4-17 使用了默认名称空间的book4.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.sunxin.org/book"
xmlns="http://www.sunxin.org/book">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
我们在xs:schema元素上声明了默认名称空间http://www.sunxin.org/book,因此在声明book元素时,引用bookType类型就无须再添加前缀了。
符合上述模式文档的XML实例文档如例4-18所示。
例4-18 book.xml
<?xml version="1.0" encoding="GB2312"?>
<book:book xmlns:book="http://www.sunxin.org/book" isbn="978-7-121-06812-6">
<title>《Struts 2深入详解》</title>
<author>孙鑫</author>
</book:book>
在例4-17的模式文档中,在bookType类型定义中声明的元素title和author是局部元素,没有被目标名称空间所限定,因此在实例文档中,对title和author元素不要添加book前缀。局部元素和属性的限定
在例4-17中,局部元素title和author没有被目标名称空间所限定,如果要限定局部元素和属性,可以通过xs:schema元素的elementFormDefault和attibuteFormDefault属性来设置。
为了指定模式文档中局部声明的元素必须被限定,可以将xs:schema元素的elementFormDefault属性的值设为"qualified"。
将例4-17中的xs:schema元素修改为如例4-19所示。
例4-19 book5.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.sunxin.org/book"
elementFormDefault="qualified">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
elementFormDefault属性的默认值是unqualified,即对局部声明的元素不加限定。
我们修改了模式文档,相应的就要修改实例文档,如例4-20所示。
例4-20 book2.xml
<?xml version="1.0" encoding="GB2312"?>
<book:book xmlns:book="http://www.sunxin.org/book" isbn="978-7-121-06812-6">
<book:title>《Struts 2深入详解》</book:title>
<book:author>孙鑫</book:author>
</book:book>
在例4-20的实例文档中,所有的元素都属于同一个名称空间,因此我们可以声明一个默认的名称空间,来省略前缀的使用,如例4-21所示。
例4-21 book3.xml
<?xml version="1.0" encoding="GB2312"?>
<book xmlns="http://www.sunxin.org/book" isbn="978-7-121-06812-6">
<title>《Struts 2深入详解》</title>
<author>孙鑫</author>
</book>
属性的限定和元素的限定是类似的。如果属性被声明为全局属性或者xs:schema元素的attibuteFormDefault属性被设置成"qualified"的话,那么属性就必须被限定,在实例文档中将以带名称空间前缀的方式出现。实际上,需要限定的属性必须明确的加上名称空间前缀,因为XML名称空间推荐标准中并没有给出关于属性的默认名称空间的机制,一个没有前缀的属性将不在任何的名称空间中。属性是附属于元素的,对属性添加名称空间不是很有必要,因此在多数应用中都没有给属性添加限定。
使用了attibuteFormDefault属性的模式文档,如例4-22所示。
例4-22 book6.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.sunxin.org/book"
targetNamespace="http://www.sunxin.org/book"
elementFormDefault="qualified"
attributeFormDefault="qualified">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
attributeFormDefault属性的默认值是unqualified,即对局部声明的属性不加限定。
一个符合模式(例4-22)的实例文档如例4-23所示。
例4-23 book4.xml
<?xml version="1.0" encoding="GB2312"?>
<book:book xmlns:book="http://www.sunxin.org/book" book:isbn="978-7-121-06812-6">
<book:title>《Struts 2深入详解》</book:title>
<book:author>孙鑫</book:author>
</book:book>
除了使用xs:schema元素的elementFormDefault和attibuteFormDefault属性来限定局部元素和局部属性外,我们还可以在xs:element和xs:attribute元素上分别使用form属性来限定局部元素和局部属性。我们看例4-24。
例4-24 book7.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.sunxin.org/book">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string" form="qualified"/>
<xs:element name="author" type="xs:string" form="qualified"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token" form="qualified"/>
</xs:complexType>
</xs:schema>
例4-24所示的模式文档和例4-22所示的模式文档的效果是一样的。
要注意的是,在xs:schema元素上使用elementFormDefault和attibuteFormDefault属性,影响的是整个模式文档中的局部元素和局部属性,而在xs:element和xs:attribute元素上使用form属性,影响的是当前的局部元素和局部属性。
未声明的目标名称空间
对于很多不使用名称空间的XML文档,你需要使用没有声明目标名称空间的模式文档。我们看例4-25。
例4-25 book8.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:token"/>
</xs:complexType>
</xs:schema>
在这个模式文档中我们没有声明目标名称空间,因此定义的bookType类型和声明的book元素都是没有名称空间限定的。在引用类型或元素时,不需要添加任何的名称空间前缀。在本例中,声明book元素时,type属性直接引用了bookType类型。与之相对的是,例4-25的模式文档中使用的所有XML Schema元素和类型都是通过与XML Schema名称空间相关联的名称空间前缀"xs"来明确进行限定的。
一个符合模式(例4-25)的实例文档如例4-26所示。
例4-26 book5.xml
<?xml version="1.0" encoding="GB2312"?>
<book isbn="978-7-121-06812-6">
<title>《Struts 2深入详解》</title>
<author>孙鑫</author>
</book>
要注意的是,在没有声明目标名称空间的模式文档中,强烈建议对所有的XML Schema元素和类型使用一个和XML Schema名称空间相关联的名称空间前缀(如xs或xsd)来明确实施限定。如果你对XML Schema的元素和类型使用了默认名称空间,那么对XML Schema类型的引用也许不能和对用户自定义类型的引用相区分,从而导致模式文档出现错误。例4-27错误的使用了默认名称空间,使得对自定义类型的引用和对XML Schema类型的引用不能区分。
例4-27 book9.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<element name="book" type="bookType"/>
<complexType name="bookType">
<sequence>
<element name="title" type="string"/>
<element name="author" type="string"/>
</sequence>
<attribute name="isbn" type="token"/>
</complexType>
</schema>
'bookType' must refer to an existing simple or complex type.
这是因为模式验证器认为对bookType类型的引用(没有前缀的引用),引用的是默认名称空间(即http://www.w3.org/2001/XMLSchema名称空间)中的类型,而XML Schema名称空间中并没有bookType这种类型,因此就报出了错误。
什么是Schema?
在计算机软件中,Schema这个词在不同的应用中有不同的含义,可以翻译为:架构、结构、规则、模式等。在XML中,Schema指的是定义和描述XML文档的规则,翻译为模式。
XML Schema与DTD的比较
我们看例4-3所示的XML文档。
例4-3 employee.xml
<?xml version="1.0" encoding="GB2312"?>
<employee sn="hr-01-001">
<name>张三</name>
<age>26</age>
<email>zhangsan@sunxin.org</email>
</employee>
如果采用DTD来定义例4-3的XML文档的结构,则一个可能的DTD如例4-4所示。
例4-4 employee.dtd
<!ELEMENT employee (name,age,email)>①
<!ELEMENT name (#PCDATA)>②
<!ELEMENT age (#PCDATA)>③
<!ELEMENT email (#PCDATA)>④
<!ATTLIST employee sn CDATA #required>⑤
这个DTD表示employee元素可以有三个子元素name、age和email,这三个子元素必须依次出现,它们的内容只能是字符数据。employee元素还有一个必需的属性sn,属性值为字符数据。
如果我们想要在上述DTD定义的基础上进一步限制元素的内容,例如,限制age元素的内容只能是正整数,或者限制email元素的内容必须是有效的邮件格式,DTD就无能为力了。下面我们采用XML Schema来定义例4-3的XML文档的结构并约束文档的内容,如例4-5所示。
例4-5 employee.xsd
<?xml version="1.0" encoding="GB2312"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="employee" type="empType"/>①
<xs:complexType name="empType">②
<xs:sequence>③
<xs:element name="name" type="xs:string"/>④
<xs:element name="age" type="xs:positiveInteger"/>⑤
<xs:element name="email">⑥
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern ⑦
value="[a-z0-9A-Z]+([-|\.]?[a-z0-9A-Z])*@([a-z0-9A-Z]+
(-[a-z0-9A-Z]+)?\.)+ [a-zA-Z]{2,}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<xs:attribute name="sn" type="xs:string" use="required"/>⑧
</xs:complexType>
</xs:schema>
① 使用XSDL中的xs:element元素来声明employee元素,它的类型为empType,这是自定义的类型,在②处定义。
② 使用XSDL中的xs:complexType元素来定义复杂类型,属性name指定自定义类型的名字,该名字可以被xs:element元素的type属性所引用。
③ XSDL中的xs:sequence元素用于指定在它内部声明的元素必须按照声明的顺序出现。①、②、③合起来与例4-4中的①(<!ELEMENT employee (name,email)>)对应。
④ 声明name元素,其内容只能是字符串值。与例4-4中的②(<!ELEMENT name (#PCDATA)>)对应。
⑤ 声明age元素,其内容只能是正整数。与例4-4中的③(<!ELEMENT age (#PCDATA)>)对应。
⑥ 声明email元素,使用正则表达式对email元素的内容做了限定,要求内容必须符合邮件格式的规范。与例4-4中的④(<!ELEMENT email (#PCDATA)>)对应。
⑦ XSDL中的xs:pattern元素使用正则表达式来限制值的范围。
⑧ 声明sn属性,属性值是字符串,该属性是必需的(use="required")。与例4-4中的⑤(<!ATTLIST employee sn CDATA #required>)对应。
从例4-5的XML Schema的定义可以看出,XML Schema具有丰富的数据类型,除了内置的数据类型外(例如:xs:string、xs:positiveInteger),我们还可以定义自己的数据类型(例如:empType)。
如果以建筑图纸与建筑的关系为例来比较XML Schema和DTD,那么DTD只能定义厨房、阳台的位置,主卧的结构等,而XML Schema还可以定义卧室使用什么类型的地板、阳台选用什么类型的铝合窗,即XML Schema定义了丰富的数据类型。
DTD不能很好地满足XML自动化处理的要求(由于采用了非XML语法格式),缺乏对文档结构、元素、属性、数据类型等约束的足够描述。与XML Schema相比,DTD具有以下的局限性:
(2)约束定义能力不足,无法对XML实例文档做出更细致的语义限制;
(3)DTD定义不够结构化,重用的代价相对较高;
(4)不使用XML语法,无法采用一致的方式来处理XML文档和DTD。
术语明晰
XML Schema中有一些术语不是很好理解,为了便于后续内容的学习,这一节我们首先明确一些术语的含义。
模式
模式指的是依照XML Schema规范,使用http://www.w3.org/2001/XMLSchema名称空间中的元素来描述XML文档结构的规则集。模式由许多不同类型的组件构成,包括:元素、属性、简单类型、复杂类型、记号(notation)、元素组(模型组定义)、属性组和标识约束等。
模式在模式文档中定义,模式文档通常以文件的形式存在,扩展名为.xsd。不过,模式文档不是必须要以文件的形式存在,它们在应用程序中可以以字节流的形式存在,也可以作为数据库记录而存在。
每个模式定义都以一个根元素xs:schema开始,该元素属于http://www.w3.org/2001/XMLSchema名称空间。
实例和模式
模式用于定义XML文档的结构,并对文档的内容进行约束,符合某个模式的XML文档称为实例。检查一个实例文档是否符合一个或多个模式的过程,称为模式验证(schema validation)。包含模式定义的文档称为模式文档,符合某个特定模式的XML文档称为实例文档。
声明和定义
在XML Schema推荐标准中,使用了"声明"和"定义"两个术语。对于出现在实例文档中并通过名称来验证的组件使用"声明",这些组件包括元素、属性、记号(notation),如元素声明、属性声明。对于在模式内部中的组件使用"定义",这些组件包括数据类型、元素组、属性组和一致性约束,如数据类型定义,元素组定义。
元素和属性的声明
元素和属性分别通过http://www.w3.org/2001/XMLSchema名称空间中的element和attribute元素来声明,如例4-6所示。
例4-6 在模式文档中声明元素和属性
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="student" type="xs:string"/>
<xs:attribute name="sn" type="xs:integer"/>
</xs:schema>
元素和属性的类型
我们看例4-7的XML文档。
例4-7 desk.xml
<?xml version="1.0" encoding="GB2312"?>
<desk length="1.2" width="0.6" color="white">
<name>办公桌</name>
<drawer>
<amount>2</amount>
</drawer>
</desk>
从本质上来说,XML文档只能保存文本格式的数据,例如,length属性的值1.2和amount元素的内容2,都是字符形式,然而,为了让XML文档能够进行数据交换(如与数据库或程序内存中的数据进行交换),需要人为地为XML文档中的数据定义一些数据类型,以方便对元素内容和属性值进行验证。DTD对数据类型的支持是非常有限的,而XML Schema则提供了丰富的数据类型,并且允许自定义数据类型。
在XML Schema中,元素和属性都有数据类型,元素的数据类型即元素内容的数据类型,属性的数据类型即属性值的数据类型。XML Schema把元素和属性的概念与其数据类型分开,这就允许不同名称的元素拥有相同类型的数据。例如,我们可以声明两个元素homeAddress和officeAddress,它们具有相同的结构,但名称不同,我们只需要定义一种数据类型addressType,然后在两个元素的声明中引用它,如例4-8所示。
例4-8 元素homeAddress和officeAddress具有相同的类型
<xs:element name="homeAddress" type="addressType"/>
<xs:element name="officeAddress" type="addressType"/>
<xs:complexType name="addressType">
<xs:sequence>
<xs:element name="street" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
元素如果包含子元素或者带有属性则称为具有复杂类型,如例4-7中的desk和drawer元素;反之元素如果仅仅包含字符数据(可以是数字、字符串、日期或者其他数据类型),不包含任何子元素,也不带有属性则称为具有简单类型,如例4-7中的name和amount元素。属性总是具有简单类型,因为属性不能有子元素或其他属性。
简单类型
具有简单类型的元素只能包含字符数据,不能包含子元素,也不能有属性。XML Schema推荐标准内置了44种简单类型,如例4-6、例4-8中的xs:string、xs:integer等,这些简单类型可以直接引用。XML Schema内置的数据类型的层次结构如图 4-1所示。
XML Schema支持类型的派生,这和面向对象语言中对象的继承有些类似,但又不完全相同。XML Schema类型的派生分为限制(restriction)和扩展(extension),通过限制派生的新类型的值范围是原类型值范围的子集,通过扩展则可以为现有类型添加新的元素和属性。
对于简单类型,只有限制派生而没有扩展派生,我们只能通过限制一个现有的简单类型(内置的简单类型或派生于内置简单类型的简单类型)来派生一个新的简单类型。那如何对现有的简单类型进行限制呢?XML Schema给我们提供了12个面(facet),用于指定一个值的有效范围、约束值的长度和精度、枚举一系列的有效值,或者指定有效值必须匹配的正则表达式。
图4-1 内置数据类型的层次结构
面与类型的关系就好像棱镜的面与棱镜的关系,透过不同的面,你看到的景观(内容)是不同的。在XML Schema中,面相当于是对一个现有的简单类型添加某些约束(限制),约束不同,得到的结果就不同。也就是说,你可以把面(facet)看成是对数据类型的约束,目的是为了让它的值限制在一定的范围内。实际上,这些面就是http://www.w3.org/2001/XMLSchema名称空间中的一些元素,只不过这些元素是用来对现有类型的值进行约束的,因而称为面。
要定义新的简单类型,使用xs:simpleType元素,要对现有的基类型进行限制,使用xs:restriction元素,并在该元素的内部使用限制值范围的面(facet)。例如,我们定义一个雇员年龄的类型,雇员年龄要求在18~60岁之间,我们可以从内置的xs:integer类型派生一个新的简单类型ageType,然后使用xs:minInclusive和xs:maxInclusive面来限制年龄值的范围,如例4-9所示。
例4-9 使用minInclusive和maxInclusive面来限制年龄值的范围
<xs:simpleType name="ageType">
<xs:restriction base="xs:integer">①
<xs:minInclusive value="18"/>
<xs:maxInclusive value="60"/>
</xs:restriction>
</xs:simpleType>
① xs:restriction元素的base属性指定要限制的基类型
复杂类型
具有复杂类型的元素可以有子元素和属性。复杂类型使用xs:complexType元素来定义。
复杂类型要么具有简单内容,要么具有复杂内容。元素的"内容"是指在它的开始标签和结束标签之间的字符数据和子元素。简单内容指的是它只包含字符数据内容,而没有子元素(可以有属性),简单内容使用xs:simpleContent元素来定义;除此之外的元素内容称为复杂内容,复杂内容使用xs:complexContent元素来定义。
例如,对于例4-10所示的具有简单内容的book元素,它的复杂类型定义如例4-11所示。
例4-10 实例文档中的book元素
<book isbn= "978-7-121-06812-6">Struts 2深入详解</book>
例4-11 模式文档中的bookType类型定义
<xs:element name="book" type="bookType"/>
<xs:complexType name="bookType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="isbn" type="xs:token"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
对于例4-12所示的具有复杂内容的student元素,它的复杂类型定义如例4-13所示。
例4-12 实例文档中的student元素
<student>
<name>张三</name>
<age>20</age>
</student>
例4-13 模式文档中的studentType类型定义
<xs:element name="book" type="studentType"/>
<xs:complexType name="studentType">
<xs:complexContent>
<xs:restriction base="xs:anyType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:integer"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
什么是Schema?
在计算机软件中,Schema这个词在不同的应用中有不同的含义,可以翻译为:架构、结构、规则、模式等。在XML中,Schema指的是定义和描述XML文档的规则,翻译为模式。
XML Schema与DTD的比较
我们看例4-3所示的XML文档。
例4-3 employee.xml
<?xml version="1.0" encoding="GB2312"?> <employee sn="hr-01-001"> <name>张三</name> <age>26</age> <email>zhangsan@sunxin.org</email> </employee> |
如果采用DTD来定义例4-3的XML文档的结构,则一个可能的DTD如例4-4所示。
例4-4 employee.dtd
<!ELEMENT employee (name,age,email)>① <!ELEMENT name (#PCDATA)>② <!ELEMENT age (#PCDATA)>③ <!ELEMENT email (#PCDATA)>④ <!ATTLIST employee sn CDATA #required>⑤ |
这个DTD表示employee元素可以有三个子元素name、age和email,这三个子元素必须依次出现,它们的内容只能是字符数据。employee元素还有一个必需的属性sn,属性值为字符数据。
如果我们想要在上述DTD定义的基础上进一步限制元素的内容,例如,限制age元素的内容只能是正整数,或者限制email元素的内容必须是有效的邮件格式,DTD就无能为力了。下面我们采用XML Schema来定义例4-3的XML文档的结构并约束文档的内容,如例4-5所示。
例4-5 employee.xsd
<?xml version="1.0" encoding="GB2312"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="employee" type="empType"/>① <xs:complexType name="empType">② <xs:sequence>③ <xs:element name="name" type="xs:string"/>④ <xs:element name="age" type="xs:positiveInteger"/>⑤ <xs:element name="email">⑥ <xs:simpleType> <xs:restriction base="xs:string"> <xs:pattern ⑦ value="[a-z0-9A-Z]+([-|\.]?[a-z0-9A-Z])*@([a-z0-9A-Z]+ (-[a-z0-9A-Z]+)?\.)+ [a-zA-Z]{2,}"/> </xs:restriction> </xs:simpleType> </xs:element> </xs:sequence> <xs:attribute name="sn" type="xs:string" use="required"/>⑧ </xs:complexType> </xs:schema> |
① 使用XSDL中的xs:element元素来声明employee元素,它的类型为empType,这是自定义的类型,在②处定义。
② 使用XSDL中的xs:complexType元素来定义复杂类型,属性name指定自定义类型的名字,该名字可以被xs:element元素的type属性所引用。
③ XSDL中的xs:sequence元素用于指定在它内部声明的元素必须按照声明的顺序出现。①、②、③合起来与例4-4中的①(<!ELEMENT employee (name,email)>)对应。
④ 声明name元素,其内容只能是字符串值。与例4-4中的②(<!ELEMENT name (#PCDATA)>)对应。
⑤ 声明age元素,其内容只能是正整数。与例4-4中的③(<!ELEMENT age (#PCDATA)>)对应。
⑥ 声明email元素,使用正则表达式对email元素的内容做了限定,要求内容必须符合邮件格式的规范。与例4-4中的④(<!ELEMENT email (#PCDATA)>)对应。
⑦ XSDL中的xs:pattern元素使用正则表达式来限制值的范围。
⑧ 声明sn属性,属性值是字符串,该属性是必需的(use="required")。与例4-4中的⑤(<!ATTLIST employee sn CDATA #required>)对应。
从例4-5的XML Schema的定义可以看出,XML Schema具有丰富的数据类型,除了内置的数据类型外(例如:xs:string、xs:positiveInteger),我们还可以定义自己的数据类型(例如:empType)。
如果以建筑图纸与建筑的关系为例来比较XML Schema和DTD,那么DTD只能定义厨房、阳台的位置,主卧的结构等,而XML Schema还可以定义卧室使用什么类型的地板、阳台选用什么类型的铝合窗,即XML Schema定义了丰富的数据类型。
DTD不能很好地满足XML自动化处理的要求(由于采用了非XML语法格式),缺乏对文档结构、元素、属性、数据类型等约束的足够描述。与XML Schema相比,DTD具有以下的局限性:
(2)约束定义能力不足,无法对XML实例文档做出更细致的语义限制;
(3)DTD定义不够结构化,重用的代价相对较高;
(4)不使用XML语法,无法采用一致的方式来处理XML文档和DTD。
术语明晰
XML Schema中有一些术语不是很好理解,为了便于后续内容的学习,这一节我们首先明确一些术语的含义。
模式
模式指的是依照XML Schema规范,使用http://www.w3.org/2001/XMLSchema名称空间中的元素来描述XML文档结构的规则集。模式由许多不同类型的组件构成,包括:元素、属性、简单类型、复杂类型、记号(notation)、元素组(模型组定义)、属性组和标识约束等。
模式在模式文档中定义,模式文档通常以文件的形式存在,扩展名为.xsd。不过,模式文档不是必须要以文件的形式存在,它们在应用程序中可以以字节流的形式存在,也可以作为数据库记录而存在。
每个模式定义都以一个根元素xs:schema开始,该元素属于http://www.w3.org/2001/XMLSchema名称空间。
实例和模式
模式用于定义XML文档的结构,并对文档的内容进行约束,符合某个模式的XML文档称为实例。检查一个实例文档是否符合一个或多个模式的过程,称为模式验证(schema validation)。包含模式定义的文档称为模式文档,符合某个特定模式的XML文档称为实例文档。
声明和定义
在XML Schema推荐标准中,使用了"声明"和"定义"两个术语。对于出现在实例文档中并通过名称来验证的组件使用"声明",这些组件包括元素、属性、记号(notation),如元素声明、属性声明。对于在模式内部中的组件使用"定义",这些组件包括数据类型、元素组、属性组和一致性约束,如数据类型定义,元素组定义。
元素和属性的声明
元素和属性分别通过http://www.w3.org/2001/XMLSchema名称空间中的element和attribute元素来声明,如例4-6所示。
例4-6 在模式文档中声明元素和属性
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="student" type="xs:string"/> <xs:attribute name="sn" type="xs:integer"/> </xs:schema> |
元素和属性的类型
我们看例4-7的XML文档。
例4-7 desk.xml
<?xml version="1.0" encoding="GB2312"?> <desk length="1.2" width="0.6" color="white"> <name>办公桌</name> <drawer> <amount>2</amount> </drawer> </desk> |
从本质上来说,XML文档只能保存文本格式的数据,例如,length属性的值1.2和amount元素的内容2,都是字符形式,然而,为了让XML文档能够进行数据交换(如与数据库或程序内存中的数据进行交换),需要人为地为XML文档中的数据定义一些数据类型,以方便对元素内容和属性值进行验证。DTD对数据类型的支持是非常有限的,而XML Schema则提供了丰富的数据类型,并且允许自定义数据类型。
在XML Schema中,元素和属性都有数据类型,元素的数据类型即元素内容的数据类型,属性的数据类型即属性值的数据类型。XML Schema把元素和属性的概念与其数据类型分开,这就允许不同名称的元素拥有相同类型的数据。例如,我们可以声明两个元素homeAddress和officeAddress,它们具有相同的结构,但名称不同,我们只需要定义一种数据类型addressType,然后在两个元素的声明中引用它,如例4-8所示。
例4-8 元素homeAddress和officeAddress具有相同的类型
<xs:element name="homeAddress" type="addressType"/> <xs:element name="officeAddress" type="addressType"/> <xs:complexType name="addressType"> <xs:sequence> <xs:element name="street" type="xs:string"/> <xs:element name="city" type="xs:string"/> <xs:element name="country" type="xs:string"/> </xs:sequence> </xs:complexType> |
元素如果包含子元素或者带有属性则称为具有复杂类型,如例4-7中的desk和drawer元素;反之元素如果仅仅包含字符数据(可以是数字、字符串、日期或者其他数据类型),不包含任何子元素,也不带有属性则称为具有简单类型,如例4-7中的name和amount元素。属性总是具有简单类型,因为属性不能有子元素或其他属性。
简单类型
具有简单类型的元素只能包含字符数据,不能包含子元素,也不能有属性。XML Schema推荐标准内置了44种简单类型,如例4-6、例4-8中的xs:string、xs:integer等,这些简单类型可以直接引用。XML Schema内置的数据类型的层次结构如图 4-1所示。
XML Schema支持类型的派生,这和面向对象语言中对象的继承有些类似,但又不完全相同。XML Schema类型的派生分为限制(restriction)和扩展(extension),通过限制派生的新类型的值范围是原类型值范围的子集,通过扩展则可以为现有类型添加新的元素和属性。
对于简单类型,只有限制派生而没有扩展派生,我们只能通过限制一个现有的简单类型(内置的简单类型或派生于内置简单类型的简单类型)来派生一个新的简单类型。那如何对现有的简单类型进行限制呢?XML Schema给我们提供了12个面(facet),用于指定一个值的有效范围、约束值的长度和精度、枚举一系列的有效值,或者指定有效值必须匹配的正则表达式。
图4-1 内置数据类型的层次结构 |
面与类型的关系就好像棱镜的面与棱镜的关系,透过不同的面,你看到的景观(内容)是不同的。在XML Schema中,面相当于是对一个现有的简单类型添加某些约束(限制),约束不同,得到的结果就不同。也就是说,你可以把面(facet)看成是对数据类型的约束,目的是为了让它的值限制在一定的范围内。实际上,这些面就是http://www.w3.org/2001/XMLSchema名称空间中的一些元素,只不过这些元素是用来对现有类型的值进行约束的,因而称为面。
要定义新的简单类型,使用xs:simpleType元素,要对现有的基类型进行限制,使用xs:restriction元素,并在该元素的内部使用限制值范围的面(facet)。例如,我们定义一个雇员年龄的类型,雇员年龄要求在18~60岁之间,我们可以从内置的xs:integer类型派生一个新的简单类型ageType,然后使用xs:minInclusive和xs:maxInclusive面来限制年龄值的范围,如例4-9所示。
例4-9 使用minInclusive和maxInclusive面来限制年龄值的范围
<xs:simpleType name="ageType"> <xs:restriction base="xs:integer">① <xs:minInclusive value="18"/> <xs:maxInclusive value="60"/> </xs:restriction> </xs:simpleType> |
① xs:restriction元素的base属性指定要限制的基类型
复杂类型
具有复杂类型的元素可以有子元素和属性。复杂类型使用xs:complexType元素来定义。
复杂类型要么具有简单内容,要么具有复杂内容。元素的"内容"是指在它的开始标签和结束标签之间的字符数据和子元素。简单内容指的是它只包含字符数据内容,而没有子元素(可以有属性),简单内容使用xs:simpleContent元素来定义;除此之外的元素内容称为复杂内容,复杂内容使用xs:complexContent元素来定义。
例如,对于例4-10所示的具有简单内容的book元素,它的复杂类型定义如例4-11所示。
例4-10 实例文档中的book元素
<book isbn= "978-7-121-06812-6">Struts 2深入详解</book> |
例4-11 模式文档中的bookType类型定义
<xs:element name="book" type="bookType"/> <xs:complexType name="bookType"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute name="isbn" type="xs:token"/> </xs:extension> </xs:simpleContent> </xs:complexType> |
对于例4-12所示的具有复杂内容的student元素,它的复杂类型定义如例4-13所示。
例4-12 实例文档中的student元素
<student> <name>张三</name> <age>20</age> </student> |
例4-13 模式文档中的studentType类型定义
<xs:element name="book" type="studentType"/> <xs:complexType name="studentType"> <xs:complexContent> <xs:restriction base="xs:anyType"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="age" type="xs:integer"/> </xs:sequence> </xs:restriction> </xs:complexContent> </xs:complexType> |
XMLSchema 模式与命名空间
模式与名称空间
DTD的问题就在于和名称空间不相关,当你为一个XML文档使用了名称空间后,文档的DTD就需要重写。XML Schema对名称空间提供了很好的支持。在编写模式文档时,你可以指定该模式文档是为哪一个名称空间声明和定义组件,这个名称空间称为目标名称空间。
4.5.6.1 目标名称空间
目标名称空间使用xs:schema元素的targetNamespace属性来指定,如例4-16所示。
例4-16 book3.xsd
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.sunxin.org/book"① xmlns:book="http://www.sunxin.org/book">② <xs:element name="book" type="book:bookType"/>③ <xs:complexType name="bookType">④ <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> </xs:sequence> <xs:attribute name="isbn" type="xs:token"/> </xs:complexType> </xs:schema> |
① 我们在xs:schema元素上使用targetNamespace属性指定目标名称空间为:http://www.sunxin.org/book,即指明了在该模式文档中声明的元素、定义的类型都属于http://www.sunxin.org/book名称空间。
② 声明名称空间http://www.sunxin.org/book,并为该名称空间绑定前缀book,使用该前缀来引用http://www.sunxin.org/book名称空间中的类型。
③ 由于指定了目标名称空间,文档中定义的类型都属于目标名称空间,因此在引用类型时,需要加上book前缀。
④ 在指定类型名时,不需要加上任何前缀,当使用了targetNamespace属性后,在模式文档中定义的任何类型都属于目标名称空间。
在模式文档中,我们很容易就能区分出不同名称空间中的元素和类型,带有xs前缀的元素和类型属于http://www.w3.org/2001/XMLSchema名称空间,而其他的元素和类型则属于目标名称空间。需要注意的是,只有模式文档中的全局元素和全局属性才属于目标名称空间,在本例中,声明的全局元素name属于目标名称空间,而局部元素title和author则不属于目标名称空间。
为了简化模式文档对目标名称空间中的元素和类型的引用,我们可以利用默认名称空间来简化引用,例4-16如果使用了默认名称空间,可以修改为如例4-17所示。
例4-17 使用了默认名称空间的book4.xsd
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.sunxin.org/book" xmlns="http://www.sunxin.org/book"> <xs:element name="book" type="bookType"/> <xs:complexType name="bookType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> </xs:sequence> <xs:attribute name="isbn" type="xs:token"/> </xs:complexType> </xs:schema> |
我们在xs:schema元素上声明了默认名称空间http://www.sunxin.org/book,因此在声明book元素时,引用bookType类型就无须再添加前缀了。
符合上述模式文档的XML实例文档如例4-18所示。
例4-18 book.xml
<?xml version="1.0" encoding="GB2312"?> <book:book xmlns:book="http://www.sunxin.org/book" isbn="978-7-121-06812-6"> <title>《Struts 2深入详解》</title> <author>孙鑫</author> </book:book> |
在例4-17的模式文档中,在bookType类型定义中声明的元素title和author是局部元素,没有被目标名称空间所限定,因此在实例文档中,对title和author元素不要添加book前缀。局部元素和属性的限定
在例4-17中,局部元素title和author没有被目标名称空间所限定,如果要限定局部元素和属性,可以通过xs:schema元素的elementFormDefault和attibuteFormDefault属性来设置。
为了指定模式文档中局部声明的元素必须被限定,可以将xs:schema元素的elementFormDefault属性的值设为"qualified"。
将例4-17中的xs:schema元素修改为如例4-19所示。
例4-19 book5.xsd
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.sunxin.org/book" elementFormDefault="qualified"> <xs:element name="book" type="bookType"/> <xs:complexType name="bookType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> </xs:sequence> <xs:attribute name="isbn" type="xs:token"/> </xs:complexType> </xs:schema> |
elementFormDefault属性的默认值是unqualified,即对局部声明的元素不加限定。
我们修改了模式文档,相应的就要修改实例文档,如例4-20所示。
例4-20 book2.xml
<?xml version="1.0" encoding="GB2312"?> <book:book xmlns:book="http://www.sunxin.org/book" isbn="978-7-121-06812-6"> <book:title>《Struts 2深入详解》</book:title> <book:author>孙鑫</book:author> </book:book> |
在例4-20的实例文档中,所有的元素都属于同一个名称空间,因此我们可以声明一个默认的名称空间,来省略前缀的使用,如例4-21所示。
例4-21 book3.xml
<?xml version="1.0" encoding="GB2312"?> <book xmlns="http://www.sunxin.org/book" isbn="978-7-121-06812-6"> <title>《Struts 2深入详解》</title> <author>孙鑫</author> </book> |
属性的限定和元素的限定是类似的。如果属性被声明为全局属性或者xs:schema元素的attibuteFormDefault属性被设置成"qualified"的话,那么属性就必须被限定,在实例文档中将以带名称空间前缀的方式出现。实际上,需要限定的属性必须明确的加上名称空间前缀,因为XML名称空间推荐标准中并没有给出关于属性的默认名称空间的机制,一个没有前缀的属性将不在任何的名称空间中。属性是附属于元素的,对属性添加名称空间不是很有必要,因此在多数应用中都没有给属性添加限定。
使用了attibuteFormDefault属性的模式文档,如例4-22所示。
例4-22 book6.xsd
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.sunxin.org/book" targetNamespace="http://www.sunxin.org/book" elementFormDefault="qualified" attributeFormDefault="qualified"> <xs:element name="book" type="bookType"/> <xs:complexType name="bookType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> </xs:sequence> <xs:attribute name="isbn" type="xs:token"/> </xs:complexType> </xs:schema> |
attributeFormDefault属性的默认值是unqualified,即对局部声明的属性不加限定。
一个符合模式(例4-22)的实例文档如例4-23所示。
例4-23 book4.xml
<?xml version="1.0" encoding="GB2312"?> <book:book xmlns:book="http://www.sunxin.org/book" book:isbn="978-7-121-06812-6"> <book:title>《Struts 2深入详解》</book:title> <book:author>孙鑫</book:author> </book:book> |
除了使用xs:schema元素的elementFormDefault和attibuteFormDefault属性来限定局部元素和局部属性外,我们还可以在xs:element和xs:attribute元素上分别使用form属性来限定局部元素和局部属性。我们看例4-24。
例4-24 book7.xsd
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.sunxin.org/book"> <xs:element name="book" type="bookType"/> <xs:complexType name="bookType"> <xs:sequence> <xs:element name="title" type="xs:string" form="qualified"/> <xs:element name="author" type="xs:string" form="qualified"/> </xs:sequence> <xs:attribute name="isbn" type="xs:token" form="qualified"/> </xs:complexType> </xs:schema> |
例4-24所示的模式文档和例4-22所示的模式文档的效果是一样的。
要注意的是,在xs:schema元素上使用elementFormDefault和attibuteFormDefault属性,影响的是整个模式文档中的局部元素和局部属性,而在xs:element和xs:attribute元素上使用form属性,影响的是当前的局部元素和局部属性。
未声明的目标名称空间
对于很多不使用名称空间的XML文档,你需要使用没有声明目标名称空间的模式文档。我们看例4-25。
例4-25 book8.xsd
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="book" type="bookType"/> <xs:complexType name="bookType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> </xs:sequence> <xs:attribute name="isbn" type="xs:token"/> </xs:complexType> </xs:schema> |
在这个模式文档中我们没有声明目标名称空间,因此定义的bookType类型和声明的book元素都是没有名称空间限定的。在引用类型或元素时,不需要添加任何的名称空间前缀。在本例中,声明book元素时,type属性直接引用了bookType类型。与之相对的是,例4-25的模式文档中使用的所有XML Schema元素和类型都是通过与XML Schema名称空间相关联的名称空间前缀"xs"来明确进行限定的。
一个符合模式(例4-25)的实例文档如例4-26所示。
例4-26 book5.xml
<?xml version="1.0" encoding="GB2312"?> <book isbn="978-7-121-06812-6"> <title>《Struts 2深入详解》</title> <author>孙鑫</author> </book> |
要注意的是,在没有声明目标名称空间的模式文档中,强烈建议对所有的XML Schema元素和类型使用一个和XML Schema名称空间相关联的名称空间前缀(如xs或xsd)来明确实施限定。如果你对XML Schema的元素和类型使用了默认名称空间,那么对XML Schema类型的引用也许不能和对用户自定义类型的引用相区分,从而导致模式文档出现错误。例4-27错误的使用了默认名称空间,使得对自定义类型的引用和对XML Schema类型的引用不能区分。
例4-27 book9.xsd
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema"> <element name="book" type="bookType"/> <complexType name="bookType"> <sequence> <element name="title" type="string"/> <element name="author" type="string"/> </sequence> <attribute name="isbn" type="token"/> </complexType> </schema> |
'bookType' must refer to an existing simple or complex type. |
这是因为模式验证器认为对bookType类型的引用(没有前缀的引用),引用的是默认名称空间(即http://www.w3.org/2001/XMLSchema名称空间)中的类型,而XML Schema名称空间中并没有bookType这种类型,因此就报出了错误。
xsi:schemaLocation详解
在实例中引用模式文档
XML Schema提供了两个在实例文档中使用的特殊属性,用于指出模式文档的位置。这两个属性是:xsi:schemaLocation和xsi:noNamespaceSchemaLocation,前者用于声明了目标名称空间的模式文档,后者用于没有目标名称空间的模式文档,它们通常在实例文档中使用。
4.5.7.1 xsi:schemaLocation属性
xsi:schemaLocation属性的值由一个URI引用对组成,两个URI之间以空白符分隔。第一个URI是名称空间的名字,第二个URI给出模式文档的位置,模式处理器将从这个位置读取模式文档,该模式文档的目标名称空间必须与第一个URI相匹配。我们看例4-28。
例4-28 book6.xml
<?xml version="1.0" encoding="GB2312"?> <book xmlns="http://www.sunxin.org/book" ① xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ② xsi:schemaLocation="http://www.sunxin.org/book http://www.sunxin.org/ book.xsd">③ <title>《Struts 2深入详解》</title> <author>孙鑫</author> </book> |
① 声明默认的名称空间(http://www.sunxin.org/book)。
② 声明XML Schema实例名称空间(http://www.w3.org/2001/XMLSchema-instance),并将xsi前缀与该名称空间绑定,这样模式处理器就可以识别xsi:schemaLocation属性。XML Schema实例名称空间的前缀通常使用xsi。
③ 使用xsi:schemaLocation属性指定名称空间http://www.sunxin.org/book和模式位置http://www.sunxin.org/book.xsd相关。要注意,在这个例子中,book.xsd中声明的目标名称空间要求是http://www.sunxin.org/book。
一个可能的模式文档book.xsd如例4-29所示。
例4-29 book.xsd
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.sunxin.org/book" elementFormDefault="qualified"> <xs:element name="book" type="bookType"/> <xs:complexType name="bookType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:schema> |
实际上,xsi:schemaLocation属性的值也可以由多个URI引用对组成,每个URI引用对之间使用空白符分隔。例4-30的实例文档使用了多个名称空间,xsi:schemaLocation属性的值包含了两对URI。
例4-30 books.xml
<?xml version="1.0" encoding="GB2312"?> <books xmlns="http://www.sunxin.org/bks" xmlns:p="http://www.sunxin.org/people" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sunxin.org/bks bks.xsd http://www.sunxin.org/people people.xsd"> <book> <title>JSP深入编程</title> <author> <p:name>张三</p:name> <p:title>作家</p:title> </author> </book> <book> <title>XML从入门到精通</title> <author> <p:name>李四</p:name> <p:title>教师</p:title> </author> </book> </books> |
XML Schema推荐标准中指出,xsi:schemaLocation属性可以在实例中的任何元素上使用,而不一定是根元素,不过,xsi:schemaLocation属性必须出现在它要验证的任何元素和属性之前。
此外,要注意的是,XML Schema推荐标准并没有要求模式处理器必须要使用xsi:schemaLocation属性,某些模式处理器可以通过其他的方式来得到模式文档的位置,而忽略xsi:schemaLocation属性。
xsi:noNamespaceSchemaLocation属性
xsi:noNamespaceSchemaLocation属性用于引用没有目标名称空间的模式文档。与xsi:schemaLocation属性不同的是,xsi:noNamespaceSchemaLocation属性的值是单一的值,只是用于指定模式文档的位置。例4-31显示了在实例文档中xsi:noNamespaceSchema Location属性的使用。
例4-31 book7.xml
<?xml version="1.0" encoding="GB2312"?> <book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="book.xsd" isbn="978-7-121-06812-6" > <title>《Struts 2深入详解》</title> <author>孙鑫</author> </book> |
与xsi:schemaLocation属性一样,xsi:noNamespaceSchemaLocation属性也可以在实例中的任何元素上使用,而不一定是根元素,不过,xsi:noNamespaceSchemaLocation属性必须出现在它要验证的任何元素和属性之前。
此外,要注意的是,XML Schema推荐标准并没有要求模式处理器必须要使用xsi:noNamespaceSchemaLocation属性,某些模式处理器可以通过其他的方式来得到模式文档的位置,而忽略xsi:noNamespaceSchemaLocation属性。
XMLSchema 注解
注解
在XML文档中提供附加信息的通常方式是使用注释,即在"<!--"和"-->"之间给出注释信息。在XML Schema中,注释仍然可以使用,不过为了方便其他读者和应用程序来理解模式文档,XML Schema提供了三个元素来为模式提供注解。这三个元素是:xs:annotation、xs:documentation和xs:appinfo,其中xs:documentation和xs:appinfo是作为xs:annotation元素的子元素使用的。xs:documentation元素用于放置适合人阅读的信息,而xs:appinfo元素则用于为工具、样式表和其他应用程序提供信息。
我们看例4-32。
例4-32 hr.xsd
<?xml version="1.0" encoding="GB2312"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:annotation> <xs:documentation xml:lang="zh"> 这是一份用于企业雇员信息描述的模式文档 </xs:documentation> </xs:annotation> <xs:element name="hr"> <xs:annotation> <xs:documentation xml:lang="zh"> hr元素是文档的根元素,使用匿名类型定义来声明 </xs:documentation> </xs:annotation> <xs:complexType> <xs:annotation> <xs:documentation xml:lang="zh"> 根元素hr下可以有多个employee子元素 </xs:documentation> </xs:annotation> <xs:sequence> <xs:element name="employee" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> |
xs:annotation元素常常在元素声明或类型定义的开头使用,用于对元素的作用、类型的定义提供附加的描述信息。为了说明这些描述信息所使用的语言,可以在xs:documentation元素上使用xml:lang属性来指定语言,xml:lang属性的值是在IETF RFC 3066(Tags for the Identification of Languages)或它的后继版本中定义的语言标识符。
xs:appinfo元素用于为计算机自动处理提供信息,它没有且也不需要xml:lang属性。xs:annotation元素和xs:appinfo元素还有一个可选的source属性,它的值是一个URI引用,指向一个包含了描述信息的文档位置。