在前面一节中,已经知道在同一个XML列中存储不同结构的XML数据。这种特性有时候是要避免地。比如要用固定格式的XML列来存储来自第三方的订单信息,如果订单的格式不是事先定义好的,必将导致数据混乱。
11.3.1 什么是XML架构
在存储XML数据时,大多数情况下需要存储结构固定的XML数据。在前面的讨论中已经知道。在SQL Server 2005中XML列类型可以存储任意结构合法的XML数据。这个特性导致无法控制XML列中数据的信息。
例如如下的两条XML数据,都可以存储在同一个XML列中。
XML数据1:
<book>
<name>
童话精选集
</name>
<pages>
487
</pages>
</book>
XML数据2:
<people>
<name>
Rock
</name>
<age>
30
</age>
</people>
从内容来看,这两个XML数据分别描述的是一本书和一格人,是完全的风马牛不相及。但是更糟糕的是,这两组数据可以完全的存入到一个表的同一个XML列中。这样一来就无法知道最终这张表的XML列中究竟存储了什么东西。因为它还可以用来存储汽车的信息、房子的信息、公司的信息等等。这样的表,最终将是无法处理。
针对这种情况,XML标准制订组织W3C提供了一套详细规定XML文档结构的标准。这套标准就是XML架构,英文称做XML Schema,或简称XSD。
从本质上讲,XML架构本身也是一个XML,只不过它是用于描述其他XML数据的结构的(或者称作限定更合适)。下面看一个XML架构的例子:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name=”Book” type=”xsd:string”/>
</xsd:schema>
像下面这个标记,就是XML架构区别于一般XML信息的关键信息。
<xsd:schema xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
其中xsd被称为名字空间的名字。名字空间可以理解为是一个限定词,它限定了XML信息中的标记名称是属于一个特定的名字空间的。比如在一些XML中会出现重复的Name标记名称或者Address标记名称,其中一些可能是表示客户的名称,另一些可能表示的是客户订购的商品名称。像下面这样:
<Customer>
<Name>
Mr.Wang
</Name>
<Goods>
<Book>
<Name>
童话大全
</Name>
</Book>
</Goods>
</Customer>
从这个XML信息可以看出有两个名为Name标签,分别表示人名和书名。这有时会引起一些不必要的误解。此时就可以使用像下面这样的改进版本:
<C:Customer xmlns:C="http://MyHost/Customer" xmlns:G="http://MyHost/Goods">
<C:Name>
Mr.Wang
</C:Name>
<G:Goods>
<G:Book>
<G:Name>
童话大全
</G:Name>
</G:Book>
</G:Goods>
</C:Customer>
在这个例子中定义了两个名字空间——C和G,分别表示客户和物品。并且这两个名字空间的名字被分别用在了各自的标记前面,并用“:”隔开。尤其是两个名字都为Name的标记就被严格的区分开了。
XML规定的名字空间属性定义的格式如下:
xmlns:名字空间名称=”任意URI名称”
其中“名字空间名称”可以是任意合法的XML标识符,“URI名称”可以像例子中那样是一个格式正确的URI名称,即使这个URI名称不存在也没有关系,只要不同的名字空间用的URI不相同即可。
在XML架构中,必须使用的名字空间就是xsd,注意是小写,同时这个名字空间的URI名称必须是http://www.w3.org/2001/XMLSchema。这是因为这个名字空间已经被W3C组织定义为一个标准的名字空间,专门用于定义XML架构信息。
除了xsd命名空间,xsd还定义了一组标准的标记以及属性用于描述XML数据的结构。
知道了XML架构的名字空间xsd,那么XML架构就好理解了。本质上它也是一个XML数据,但它使用了一个标准的名字空间xsd,并用于描述特定XML数据的结构信息。
这就是XML架构的全部核心秘密,接下去将更进一步介绍XML架构在SQL Server 2005中的应用。
注意:XML架构数据存储成文件时扩展名通常是xsd,以示与普通的XML文件相区别。本质上XML架构数据仍然是一个XML数据,只不过它是被用来规定其它XML数据的结构的。通常这些文件都会被一些xsd编辑软件关联。在Windows系统中,如果安装了Virsual Studio 2003以上的版本时,这些文件都会被关联。并可以直接在这些环境中进行编辑。
11.3.2 一个基本的XML架构
先看看下面的XML数据。
<book>
小说精选集
</book>
从结构可以看出这个XML数据是非常简单的,描述它的XML架构同样简单。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name=”book” type=”xsd:string”/>
</xsd:schema>
这样前面那个简单的XML数据的结构就描述清楚了。其中xsd就是XML架构的标准官方命名空间,它的值目前必须为xmlns:xsd=http://www.w3.org/2001/XMLSchema。所有的XML架构标准元素都被定义在这个命名空间中。这个名字空间的定义部分也是XML架构数据的标准开始部分。
紧接着标准的XML架构命名空间定义之后,用xsd名字空间下的element标记定义了book这个元素。element标记就表示描述一个XML中的标记。其属性name表示被描述的标记的名称,type属性则描述了被描述标记的数据类型。在例子中book标记的数据类型为string型,既字符串。其他常用的数据类型还有xsd:float,xsd:double,xsd:long,xsd:int,xsd:date等。
11.3.3 带子元素XML架构
下面看一个带有子标记的XML数据。
<book>
<name>
读者合订本
</name>
<price>
20.00
</price>
</book>
描述它的XML架构如下。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name = "book">
<xsd:complexType>
<xsd:sequence>
<xsd:element name = "name" type = "xsd:string"/>
<xsd:element name = "price" type = "xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
其中xsd:complexType 表示前面的element标记定义的标记是一个“复杂类型”,既它至少要含有子元素。此例中表示含有子标记。特别需要注意的是这个标记的大小写方式。
嵌套在complexType标记之中的xsd:sequence标记表示其中出现的元素必须按照顺序排列。
注意:利用定义子标记的方法,可以嵌套定义更复杂的XML数据。
11.3.4 明确制定子标记的个数
在介绍XML的时候提到过XML标记是允许重名的。有时这样会遇到一个麻烦——无法明确限定子标记重复的次数。像下面的例子所示,XML数据中可以有多个叫做Worker的信息。
<Factory>
<Worker>李四</Worker>
<Worker>王五</Worker>
<Worker>赵六</Worker>
<Worker>钱七</Worker>
</Factory>
此时,可以使用element元素的maxOccurs属性和minOccurs属性来明确限定子节点的个数。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name = "Factory">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Worker" type="xsd:string" minOccurs="1" maxOccurs="10"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
在上面这个XML架构数据中,使用minOccurs属性指定了Worker这个子标记必须至少出现1次。maxOccurs则指定Worker子标记最多只能出现10次。有时如果不想限定标记个数时可以用maxOccurs= "unbounded"来指定。
需要注意的是如果在定义标记时,如果没有指定这两个属性时,此时它们的值默认都为1。
11.3.5 定义引用
从本质上讲XML描述的是一个树型的数据。如果XML数据的层数太多,那么描述它的XML架构数据将不得不也嵌套很多层。从前面的描述中可以看出为了描述一个带有子标记的XML结构,不得不使用至少三层嵌套。这样就会使XML架构的可读性,可维护性大大降低。使用象下面例子中的element标记的引用属性就可以轻松的解决这个问题。
XML数据:
<Custom>
<Name>王有钱</Name>
<Address>
<Country>中国</Country>
<Province>X省</Province>
<City>X市</City>
</Address>
</Custom>
对应的XML架构描述:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="Custom">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="Address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Province" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
这时已经可以看出,XML嵌套了3层,而描述它的XML架构却花了8层嵌套。如果使用缩排,那么排版和阅读都是非常困难的。此时可以使用element的ref属性减少嵌套层次,同时改进可读性。下面的例子和前面的XML架构等价。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="Custom">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element ref="Address"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="Address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Province" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
从嵌套层次可以看出后面这种使用了引用属性的描述方式要较前一种方式可读性强的多。
11.3.6 定义可选的子标记
如果在XML数据中某几个子标记是任选的,既不一定要全部出现时,可以使用可选子标记定义。象下面的例子中可以提供学生的学号,也可以提供学生的学号。
描述学生的XML数据象下面这样。
<Students>
<Student>
<Name>王五</Name>
<College>汉语言文学</College>
<Class>5班</Class>
</Student>
<Student>
<SID>200718277</SID>
<College>计算机信息管理</College>
<Class>28班</Class>
</Student>
</Students>
说明:在这个例子中提供了两个学生的信息。第一个提供了姓名,第二提供了学号。虽然实际中可能不会是这样,但是此处只是一个假设。目的是为了说明如何定义一个可选项。
描述它的XML架构象下面这样,同时学号和姓名被描述成了可选项。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="Students">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Student" maxOccurs="10">
<xsd:complexType>
<xsd:sequence>
<xsd:choice>
<xsd:element name="SID" type="xsd:long"/>
<xsd:element name="Name" type="xsd:string"/>
</xsd:choice>
<xsd:element name="College" type="xsd:string"/>
<xsd:element name="Class" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
11.3.7 定义属性
在有些XML标记中可以指定一些属性。通常为了跟进一步描述一个标记的确切含义,就需要为标记指定一个或多个属性来明确表达跟进一步的含义。同时通过使用标记属性元素可以在一定程度简化XML数据的结构。比如像下面这样用属性而不是使用子元素的方法来指定学生的名称和老师的名称。
<Class>
<Teacher Name="张一" Age="31"/>
<Student Name="吴七" Age="18"/>
<Student Name="赵六" Age="17"/>
<Student Name="王五" Age="19"/>
<Student Name="钱三" Age="17"/>
</Class>
可以看出这比使用XML子标记元素的方法要简单得多。并且显得非常紧凑。下面的XML架构就明确的描述了上面这个XML数据的属性及定义。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="Class">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Teacher">
<xsd:complexType>
<xsd:sequence/>
<xsd:attribute name="Name" type="xsd:string" use="required"/>
<xsd:attribute name="Age" type="xsd:int" default="25"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="Student" maxOccurs="5">
<xsd:complexType>
<xsd:sequence/>
<xsd:attribute name="Name" type="xsd:string" use="required"/>
<xsd:attribute name="Age" type="xsd:int" default="18"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
在这个XML架构新出现了一个标记xsd:attribute。这个标记就是用于定义XML标记属性的标记。从例子中可以看出定义属性和定义标记比较类似。二者都可以指定名称、类型等。其中xsd:attribute标记的use=”required”表示这个属性必须提供。而default=”18”这个属性则表示当前定义的属性默认值为18,当不提供这个属性时,其默认值就是18。
11.3.8 在XML架构中添加说明
因为XML架构本质上是一个XML数据,所以可以通过添加注释的方式来说明XML架构中的一些关键信息。但注释会使XML架构文件看上去像个写满了注释的草稿。
在XML架构中专门提供了描述文档的信息的元素:annotation、documentation、appInfo。其中后两个标记必须是annotation标记的子标记。而appinfo标记中则常常出现一些代码脚本。
下面的例子说明了如何使用XML架构文档说明。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation xml:lang="zh-cn">
这个XML架构用于说明存储客户(costomer)信息的XML数据的结构。
</xsd:documentation>
<xsd:appinfo>
//下面的java代码片断说明如何创建针对客户信息的客户类
public class costomer
{
//客户类的具体内容
......
}
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="customer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="prefecture" type="xsd:string"/>
<xsd:element name="city" type="xsd:string" />
<xsd:element name="street" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
上面例子中 documentation 标记使用了 xml 标准名字空间的一个 lang 属性名确的指定了后面的说明文字将使用简体中文。这有助于在交换 XML 架构信息时快速识别说明内容使用的是何种语言文字。在 appinfo 标记中给出一个描述 costomer 类的的 java 代码片断,这可以帮助开发人员快速的构建由该 XML 架构描述的 XML 数据的对应的类代码。同时从这些说明也可以看出,这种格式的说明比起使用注释要正式美观的多。同时由于注释常常被忽略而无法利用,例子中这种说明可以被 XML 架构解析的程序利用。