2006 年 8 月 23 日 星期三
XSD 浅学笔记
简单明快的 XSD 的入门笔记,希望能让你和我一样,用半天时间步入第一道门槛。
这一片记录基础知识,第二篇会是些进阶知识和总结,然后你就可以写出自己的第一个 XSD 文档,并用来验证一个 XML 文档了。
XSD 是什么 ?
一个 xml schema 是用来描述 xml 文档结构的,而 XSD 是 xml schema definition , 是继 DTD 后的基于 xml 的 schema 语言,比起 DTD ,它有更好的扩展性,增加了功能,更重要的是,它本身就是以 xml 来写的,而不需要象 DTD 那样重新学一门语言。同时也可以利用所有对 xml 有效的便利。
那么为什么要有 schema 呢, xml 的 well-formed 是不足够的,一个语法上合法的 xml 仍然可能有错误,而这些错误可能导致严重的应用后果。
基础概念
第一印象
以下是一份 xml 文档
<?xml version="1.0"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
而它对应的一份 xsd 会是这样的:
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3schools.com"
xmlns="http://www.w3schools.com"
elementFormDefault="qualified">
<xs:element name="note">
<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
简单元素:
简单元素是不包含其他元素和属性的元素。
定义简单元素的语法为:
<xs:element name="xxx" type="yyy"/>
常用的内建类型有:
xs:string
xs:decimal
xs:integer
xs:boolean
xs:date
xs:time
可以通过 default 属性和 fixed 属性来修饰。
<xs:element name="color" type="xs:string" default="red"/>
<xs:element name="color" type="xs:string" fixed="red"/>
属性:
只有复合元素可以有属性,但属性本身总是被定义为简单类型的。
<xs:attribute name="xxx" type="yyy"/>
除了和简单元素的定义一样可以用 default 和 fixed 来作为属性之外,还可以用 use 属性,如果把 use 属性指定为“ required ”,就说明这个属性在 xml 文档中是必须指明的,默认情况下属性的使用是可选的。
限制
当一个元素或者属性定义了类型( type ), 就可以用 restriction 来限制这个类型, restriction 又叫 facet ,可以为类型增添更多的细节要求。当 xml 文档中对应的元素值违反了这个 restriction 就不能通过检验。限制是 XSD 里比较常用的内容,我们会看大量的例子。
对数值大小的限制:(只能取 0 到 120 的闭区间)
<xs:element name="age">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="120"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
将值限制在一组既定值内:(类似于枚举 enumeration )
<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>
限制一定范围的值:(只能取小写的 26 个字母)
<xs:element name="letter">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[a-z]"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(只能取长度为 3 的大写字母组合)
<xs:element name="initials">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z][A-Z][A-Z]"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(只能取长度为 3 的字母,大小写均可)
<xs:element name="initials">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-Z][a-zA-Z][a-zA-Z]"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(只能取 xyz 中的一个)
<xs:element name="choice">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[xyz]"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(只能取长度为 5 的数字串)
<xs:element name="prodid">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:pattern value="[0-9][0-9][0-9][0-9][0-9]"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(只能取任意长度的小写字符串,长度可以为 0 )
<xs:element name="letter">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="([a-z])*"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(有了上一个,你自然也会猜到这一个:只能取长度大于 0 的字符串)
<xs:element name="letter">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="([a-z][A-Z])+"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(选取,其实和前面的枚举式功效类似,只能取其一)
<xs:element name="gender">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="male|female"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(特定长度的字符数字串,用作密码最合适。)
<xs:element name="password">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-Z0-9]{8}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(如果不需要在 base 上加内容限制,也可以这样来表达长度)
<xs:element name="password">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:length value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(设定长度区间)
<xs:element name="password">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="5"/>
<xs:maxLength value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
另外,在 xml 里,空白符的情况比较特别,不小心处理就得不到你要的真正效果。
(保留所有的空白符)
<xs:element name="address">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:whiteSpace value="preserve"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
( 所有的回车,过行,制表符,长空白都被替换成相应长度的空格符 )
<xs:element name="address">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:whiteSpace value="replace"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
(所有空白符都被变为单空格符,首尾空白都被去掉)
<xs:element name="address">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:whiteSpace value="collapse"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
最后,上面说到的所有的限制,还可以以下面这种格式来写,把 simpleType 加上名字,在元素中的 type 引用这个名字,好处就是它不再单独属于这个元素或者属性的定义了,其他元素可以复用这个类型限制,类似于面向对象编程语言中的包装与复用。
<xs:element name="car" type="carType"/>
<xs:simpleType name="carType">
<xs:restriction base="xs:string">
<xs:enumeration value="Audi"/>
<xs:enumeration value="Golf"/>
<xs:enumeration value="BMW"/>
</xs:restriction>
</xs:simpleType>
复合元素
复合元素的定义就和简单元素的刚好相反了,就是包含其他元素或属性的元素。
细分一下有四种:
空元素;
<product pid="1345"/>
只包含其他元素的;
<employee>
<firstname>John</firstname>
<lastname>Smith</lastname>
</employee>
只有文本内容的;
<food type="dessert">Ice cream</food>
同时包括文本内容和其他元素的。
<description>
It happened on <date lang="norwegian">03.03.99</date> ....
</description>
而这四个同时又都可以有属性。
接下来我们一一详细介绍。
<product prodid="1345" />
空元素的定义语法:
<xs:element name="product">
<xs:complexType>
<xs:attribute name="prodid" type="xs:positiveInteger"/>
</xs:complexType>
</xs:element>
<person>
<firstname>John</firstname>
<lastname>Smith</lastname>
</person>
包含其他元素的复合元素定义:
<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>
用 xs:sequence 来定义这些元素必须按照这个次序出现,类似于 sequence 这样的指示符还有几个,稍后统一介绍。
定义一个只有文本内容和属性的复合元素,我们需要用一个 simpleContent 标记,再用一个 restriction 或者 extension 来限制文本的内容 :
<xs:element name="somename">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="basetype">
....
....
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
或:
<xs:element name="somename">
<xs:complexType>
<xs:simpleContent>
<xs:restriction base="basetype">
....
....
</xs:restriction>
</xs:simpleContent>
</xs:complexType>
</xs:element>
例子:
<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>
注意 extension 的 integer 是限制 shoesize 的文本内容,而 attribute 的 string 是限制 attribute 本身的内容的。
对应这个复合模式的 xml 可以是:
<shoesize country=" france ">35</shoesize>
要描述混合的复合元素:
<letter>
Dear Mr.<name>John Smith</name>.
Your order <orderid>1032</orderid>
will be shipped on <shipdate> 2001-07-13 </shipdate>.
</letter>
就可以用:
<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>
只是在包含其他元素的同时指定 mixed 等于“ true “,而 sequence 或其他指示符的使用依然有效。你的文本可以穿插于元素之间,但元素的出现次序仍受 sequence 的限制。
记得简单元素里的复用吗,这里同样可以,在 complexType 标记里加上 name 属性,就完成了分离和复用了,对上面所有的复合类型都是通用的。即使写 xsd ,也要象 oop 语言那样,通过复用来提高修改的效率,同时让页面看起来整洁清晰,至少,我是有着这样的偏执的,呵呵。
指示符里有以下几种:
顺序指示符:
All 所有元素可以不分顺序出现,但只能出现一次。
Choice 只有其中一个可以出现
Sequence 前面说过了,必须按顺序出现。
上面的限制可以配合次数指示符加以进一步的限制:
maxOccurs 最多出现次数
minOccurs 最少出现次数
例如:
<xs:element name="person">
<xs:complexType>
<xs:sequence>
<xs:element name="full_name" type="xs:string"/>
<xs:element name="child_name" type="xs:string"
maxOccurs="10" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
注意用了 all 的时候,最大次数还是不能超过 1 ,最小可以是 0 或 1 。
组指示符:
Group 元素组
attributeGroup 属性组
通过例子来看看:
<xs:group name="persongroup">
<xs:sequence>
<xs:element name="firstname" type="xs:string"/>
<xs:element name="lastname" type="xs:string"/>
<xs:element name="birthday" type="xs:date"/>
</xs:sequence>
</xs:group>
定义了一组元素, group 必须包含已经用顺序指示符包含起来的元素。
定义后,别的地方可以用 ref 来引用:
<xs:complexType name="personinfo">
<xs:sequence>
<xs:group ref="persongroup"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
以下这两个是对应的 attribute group 的用法:
<xs:attributeGroup name="personattrgroup">
<xs:attribute name="firstname" type="xs:string"/>
<xs:attribute name="lastname" type="xs:string"/>
<xs:attribute name="birthday" type="xs:date"/>
</xs:attributeGroup>
<xs:element name="person">
<xs:complexType>
<xs:attributeGroup ref="personattrgroup"/>
</xs:complexType>
</xs:element>
这一篇到这里为止。