级别: 中级 Benoit Marchal (bmarchal@pineapplesoft.com), 顾问, Pineapplesoft
2003 年 3 月 01 日 W3C XML Schema 提供了许多用于构建和组织 XML 词汇表的功能强大的选项。在本技巧中,Benoit 比较了元素的全局声明和局部声明,并提供了什么时候该使用何种声明的提示。
由于 XML 开发人员需要克服 DTD 作为建模语言的限制,所以 W3C XML Schema 变得越来越流行了。XML Schema 的特别流行是由于它提供了更丰富的数据类型。使用
XML Schema,可以很容易地指定一个元素应该包含文本、数字、布尔值还是其他一些数据类型。
不过,XML Schema 提供了比数据类型更多的功能。它具有组织元素声明的选项,可以改进可读性和易维护性。在本技巧中,我将为您展示XML
Schema 中的范围选项。
局部声明与全局声明
XML Schema 让您可以直接在
xs:schema 元素下面声明元素,或者作为另一个声明的一部分声明元素。直接出现在
xs:schema 元素下面的声明具有
全局范围,而其他声明具有
局部范围。全局声明并不是新东西
-- DTD 中就已经引入它们了 -- 但是局部声明是 XML Schema 所独有的。
元素的全局声明和局部声明最明显的区别是在其他声明中可以引用全局声明,而局部声明只能存在于它们自己的局部范围内。一个实际的副作用是全局声明必须是唯一的:任何两个全局声明都不能有相同的元素名。另一方面,局部声明如果是在不同的上下文中,则不会产生冲突。甚至可以在局部覆盖一个全局声明。
为什么想要覆盖声明呢?在设计大型模式、特别是设计从现有模式中导入声明的新模式时,这是很有用的。在后一种情况下,导入的模式确实可能具有会引起冲突的声明。
例如 ,一个模式可能定义一个
key 元素为用于密钥的 base64 数据,而另一个模式将
key
定义为一个用于储存数据库标识符的整数。在这里,
key 元素有两个不同的定义,如果要创建所导入的前两者的第三个模式就会产生问题。
分析例子
清单 1中的 模式 全部使用全局声明。
<xs:element name="..."> construct
定义了一个新元素,它类似于 DTD 中的
<!ELEMENT ...> 语句。这些声明可能会通过
<xs:element
ref="..."/> construct 引用其他声明。
清单 1. 全局声明
<?xml version="1.0"?>
<xs:schema targetNamespace="http://ananas.org/2003/tips/local"
xmlns:tp="http://ananas.org/2003/tips/local"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="list">
<xs:complexType>
<xs:sequence>
<xs:element
ref="tp:movie" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="movie">
<xs:complexType>
<xs:sequence>
<xs:element
ref="tp:title"/>
<xs:element
ref="tp:genre" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="title" type="xs:string"/>
<xs:element name="genre" type="xs:string"/>
</xs:schema>
|
 |
引用和名称空间
引用一个全局元素定义时,需要提供完全限定的名字,包括相应的名称空间前缀(如
ref="tp:title" )。不过,元素声明是一个非限定名(
name="title" )。这种行为背后的逻辑(有些扭曲)是:模式中的每一个声明都自动进入目标名称空间。是的,这有些让人迷惑,但是至少警告过您了。
|
|
另一方面,清单 2尽可能地使用了局部声明。唯一的全局声明是用于文档的根的(文档的根必须是全局声明,因为根没有父元素)。所有其他声明都嵌入到根声明下面。大量的嵌套使这种方法得到了一个别名“俄罗斯娃娃”。
清单 2. 局部声明
<?xml version="1.0"?>
<xs:schema targetNamespace="http://ananas.org/2003/tips/local"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="list">
<xs:complexType>
<xs:sequence>
<xs:element name="movie" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="genre" maxOccurs="unbounded"
type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
|
注意
elementFormDefault 属性的使用。在默认情况下,XML 文档中具有局部声明的元素是非限定的(它们没有名称空间前缀)。背后的推论是非限定的元素类似于属性,因为属性在默认情况下是非限定的。
清单3展示了非限定的局部元素。在这里,
movie 、
title 和
genre
元素没有名称空间前缀,假定它们与其父元素在同一个名称空间中。对于非限定的局部元素的使用尚无定论。SOAP 成功地使用了一些,但是大多数用户认为它们是让人困惑的。
一句警告:需要将
清单
2中的
elementFormDefault 从
qualified 改为
unqualified
以验证清单 3。
清单 3. 非限定的局部元素
<?xml version="1.0"?>
<tp:list xmlns:tp="http://ananas.org/2003/tips/local">
<movie>
<title>Johnny English
</title>
<genre>comedy
</genre>
</movie>
<movie>
<title>Finding Nemo
</title>
<genre>family
</genre>
</movie>
</tp:list>
显然,如果要在文档中使用非限定的元素,那么必须使用局部元素。
得出结论
那么,局部声明和全局声明哪一个更好呢? 唉,就像设计策略的常见情况一样,答案不是这么黑白分明的,大多数模式同时使用两者。局部声明之所以有意义是因为它们隔离了模式的一部分。局部名对于其他上下文是不透明的,这使命名冲突的风险降到了最低。
局部声明的另一项好处是它们明确地标识了文档中可能的根。
局部声明的主要缺点是它们不能重复使用。在
清单
2中(使用局部声明),不能在另一个上下文中重复使用
movie 元素。在
清单
1中(使用全局声明)这是很容易的。
结论是,哪一种选择是最好的取决于元素的本性:一个准备在模式中别的地方重复使用的自包含元素最好使用全局声明,而只在给定上下文中起作用的元素应该在局部声明。
假定
movie 是一个自包含的元素,那么清单 4是一种更好的模式,它很好地结合了局部和全局声明。
清单 4. 混合声明
<?xml version="1.0"?>
<xs:schema targetNamespace="http://ananas.org/2003/tips/local"
xmlns:tp="http://ananas.org/2003/tips/local"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="list">
<xs:complexType>
<xs:sequence>
<xs:element
ref="tp:movie" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="movie">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="genre" type="xs:string"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
尽管在本技巧中没有讨论,但是您还可以在全局和本地声明中使用类型定义以改进模式的可读性。
参考资料
关于作者
对本文的评价
|