使用 XML Schema 定义元素的基本知识
级别: 初级
Ashvin Radiya, 总裁兼首席技术官, AvantSoft, Inc.
Vibha Dixit (mailto:vibha@avantsoft.com?subject=%E4%BD%BF%E7%94%A8%20XML%20Schema%20%E5%AE%9A%E4%B9%89%E5%85%83%E7%B4%A0%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%9F%A5%E8%AF%86), 首席执行官, AvantSoft, Inc.
2003 年 12 月 01 日
新的 XML Schema 系统即将成为 W3C 推荐标准,目的是为了克服 DTD 的局限性(请参阅侧栏, DTD 的局限性 ),为 XML 文档提供丰富的语法结构。本文展示了模式的灵活性,说明如何使用 XML Schema 系统来定义最基本的 XML 文档构造块——元素。
XML Schema 比 DTD 更强大。为了说明 XML Schema 机制的强大功能,下面三个程序清单简要比较了表示元素的不同方式。 清单 1给出了一个 XML 文档片段, 清单 2用 DTD 语法声明了这两个元素, 清单 3则是相应的 XML Schema 语法形式。要注意, 清单 3中所用的是相同的 XML 语法。通过模式,验证解析器可以检查元素 InvoiceNo
是否是正整数,元素 ProductID
的首字符是否为 A 到 Z 之间的字母,后面为六个阿拉伯数字。相反,引用 DTD 的验证解析器只能检查这些元素是否用字符串表示。
<InvoiceNo>123456789</InvoiceNo> <ProductID>J123456</ProductID> |
清单 2:描述清单 1 中元素的 DTD 片段
<!ELEMENT InvoiceNo (#PCDATA)> <!ELEMENT ProductID (#PCDATA)> |
清单 3:描述清单 1 中元素的 XML Schema
<element name='InvoiceNo' type='positive-integer'/> <element name='ProductID' type='ProductCode'/> <simpleType name='ProductCode' base='string'> <pattern value='[A-Z]{1}d{6}'/> </simpleType> |
|
在这个协作的世界中,一个人可能处理来自多个其他团体的文档,而不同的团体可能希望以不同的方式表示他们的数据元素。此外,他们还可能在一个文档中引用不同团体创建的同名元素。如何区分相同名字的不同定义呢?XML Schema 使用名称空间区分这些定义。
|
一个给定的 XML Schema 定义了一组新名字,如元素名、类型名、属性名、属性组名,这些名字的定义和声明都写在模式中。 清单 3定义的名字包括 InvoiceNo
、 ProductID
和 ProductCode
。
我们说模式中定义的名字属于它的 目标名称空间。名称空间本身有一个固定但没有限制的名字,必须符合 URL 语法。比如,对于 清单 3中模式片段,您可以把名称空间的名字设为: http://www.SampleStore.com/Account
。
名称空间的名字语法容易让人混淆,尽管以 http://
开始,那个 URL 并不指向一个包含模式定义的文件。事实上,这个 URL http://www.SampleStore.com/Account
根本没有指向任何文件,只是一个分配的名字。
模式中的定义和声明可能引用属于其他名称空间的名字。在本文中,我们称这些名称空间为 源名称空间。每个模式都有一个目标名称空间,但可能有多个源名称空间。名称空间的名字可能相当长,但在 XML 文档中通过 xmlns
声明可使用简写形式。为了说明这些概念,我们可以向前述 清单 4中的示例模式中添加更多的内容。
<!--XML Schema fragment in file schema1.xsd--> |
<xsd:schema targetNamespace='http://www.SampleStore.com/Account' xmlns:xsd='http://www.w3.org/1999/XMLSchema' xmlns:ACC= 'http://www.SampleStore.com/Account'> <xsd:element name='InvoiceNo' type='xsd:positive-integer'/> <xsd:element name='ProductID' type='ACC:ProductCode'/> <xsd:simpleType name='ProductCode' base='xsd:string'> <xsd:pattern value='[A-Z]{1}d{6}'/> </xsd:simpleType> |
在 清单 4的 XML Schema 中, targetNamespace
的名字是 http://www.SampleStore.com/Account
,其中包含的名字有 InvoiceNo
、 ProductID
和 ProductCode
。 schema
、 element
、 simpleType
、 pattern
、 string
和 positive-integer
这些名字属于源名称空间 http://www.w3.org/1999/XMLSchema
,通过 xmlns
声明缩写为 xsd
。别名 xsd
没有任何特殊的地方,我们可以选择任何其他的名字。在本文后面的部分为了方便和简化起见,我们使用 xsd
代表名称空间 http://www.w3.org/1999/XMLSchema
,在一些代码片段中省略了限定符 xsd
。在这个例子中, targetNamespace
偶尔也作为一个源名称空间,因为要使用名字 ProductCode
定义其他的名字。
清单 4中的模式片段不需要指定源模式文件的位置。对于整个“模式的模式”, http://www.w3.org/1999/XMLSchema
,不需要指定位置,因为它的位置是人所共知的。对于源名称空间 http://www.SampleStore.com/Account
,也不需要指定位置,因为它恰好是该文件中定义的目标名称空间。为了更好地理解如何指定模式的位置和使用默认名称空间,看一看 清单 5中扩展的例子。
<!--XML Schema fragment in file schema1.xsd--> <schema targetNamespace='http://www.SampleStore.com/Account' xmlns='http://www.w3.org/1999/XMLSchema' xmlns:ACC= 'http://www.SampleStore.com/Account' xmlns:PART= 'http://www.PartnerStore.com/PartsCatalog'> <import namespace='http://www.PartnerStore.com/PartsCatalog' schemaLocation='http://www.ProductStandards.org/repository/alpha.xsd'/> <element name='InvoiceNo' type='positive-integer'/> <element name='ProductID' type='ACC:ProductCode'/> <simpleType name='ProductCode' base='string'> <pattern value='[A-Z]{1}d{6}'/> </simpleType> <element name='stickyGlue' type='PART:SuperGlueType'/> |
清单 5中多了一个名称空间引用: http://www.PartnerStore.com/PartsCatalog
。这个名称空间不同于 targetNamespace
和标准名称空间。因此必须使用 import
声明元素引入,该元素的 schemaLocation
属性指明包含模式的文件位置。默认的名称空间是 http://www.w3.org/1999/XMLSchema
,它的 xmlns
声明没有名字。每个非限定的名字如 schema
和 element
,都属于默认名称空间 http://www.w3.org/1999/XMLSchema
。如果模式从一个名称空间中引用了多个名字,将其指定为默认名字空间更方便。
一个 XML 实例文档可能引用多个名称空间的元素名,这些名称空间定义在不同模式中。为了引用和简化名称空间的名字,同样要使用 xmlns
声明。我们使用 XML Schema 实例名称空间的 schemaLocation
属性指定文件的位置。要注意,该属性不同于上一个例子中 xsd
名称空间的同名属性 schemaLocation
。
<?xml version="1.0"?> <ACC:rootElement xmlns:ACC='http://www.SampleStore.com/Account' xmlns:PART='http://www.PartnerStore.com/PartsCatalog' xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance' xsi:schemaLocation='http://www.PartnerStore.com/PartsCatalog http://www.ProductStandards.org/repository/alpha.xsd http://www.SampleStore.com/Account http://www.SampleStore.com/repository/schema1.xsd'> <ACC:InvoiceNo>123456789</ACC:InvoiceNo> |
图 2:清单 5 和清单 6 的名称空间
|
定义元素就是定义元素的名字和内容模型。在 XML Schema 中,元素的内容模型由其类型定义,因此 XML 文档中实例元素的值必须符合模式中定义的类型。
|
类型包括简单类型和复杂类型。简单类型的值不能包含元素或属性。复杂类型可以产生在其他元素中嵌套元素的效果,或者为元素增加属性。(到目前为止本文中的例子都是用户定义的简单类型,比如 ProductCode
)。XML Schema 规范也包括预定义的简单类型(请参阅侧栏 简单类型)。 派生的简单类型约束了基类型的值。比如,派生简单类型 ProductCode
的值是基类型 string
值的子集。
不含属性或其他元素的元素可以定义为简单类型,无论是预定义的简单类型还是用户定义的简单类型,如 string
、 integer
、 decimal
、 time
、 ProductCode
等等。
<element name='age' type='integer'/> <element name='price' type='decimal'/> |
现在,试着向 清单 7中的简单元素 price
增加属性 currency
。您不能这样做,因为简单类型的元素不能有属性。如果希望增加属性,您必须把 price
元素定义成复杂类型。在 清单 8的例子中,我们定义了一个 匿名类型,没有明确地命名这个复杂类型。换句话说,没有定义复杂类型 complexType
的 name
属性。
<element name='price'> <complexType base='decimal' derivedBy='extension'> <attribute name='currency' type='string'/> </complexType> </element> <!-- In XML instance document, we can write: <price currency='US'>45.50</price> --> |
在 XML 文档中,一个元素可能嵌入其他的元素。这种要求可以在 DTD 中直接表示。但 XML Schema 定义一个元素,这个元素有一个类型,而这个类型可以包含其他元素和属性的声明。 表 1给出了一个简单的例子。
表 1:DTD 和 XML Schema 中复杂数据类型的比较
<Book> <Title>Cool XML<Title> <Author>Cool Guy</Author> </Book> |
DTD
<Book> <Title>Cool XML<Title> <Author>Cool Guy</Author> </Book> |
XML Schema
<Book> <Title>Cool XML<Title> <Author>Cool Guy</Author> </Book> |
<!ELEMENT Book (Title, Author)> <!ELEMENT Title (#PCDATA)> <!ELEMENT Author (#PCDATA)> |
<element name='Book' type='BookType'/> <complexType name='BookType'> <element name='Title' type='string'/> <element name='Author' type='string'/> </complexType> |
尽管 表 1中的 XML 代码同时满足 DTD 与 XML Schema 片段,但两者之间有一个很大的区别。在 DTD 中所有的元素都是全局性的,而表中的 XML Schema 允许把 Title
和 Author
定义成局部的——只出现在元素 Book
中。为了在 XML Schema 中实现与 DTD 声明完全相同的效果,元素 Title
和 Author
必须是全局范围的,如 清单 9中所示。元素 element
的 ref
属性使您能够引用前面声明的元素。
<element name='Title' type='string'/> <element name='Author' type='string'/> <element name='Book' type='BookType'/> <complexType name='BookType'> <element ref='Title'/> <element ref='Author'/> </complexType> |
在 表 1和 清单 9所示的例子中, BookType
是全局性的,可用于声明其他元素。相反, 清单 10将该类型局部地定义到元素 Book
中,而且定义成匿名元素。要注意, 表 1中的 XML 文档片段与表 1、 清单 9和 清单 10中三个模式片段都匹配。
<element name='Title' type='string'/> <element name='Author' type='string'/> <element name='Book'> <complexType> <element ref='Title'/> <element ref='Author'/> </complexType> </element> |
|
对于表示元素内容模型的约束,XML Schema 比 DTD 提供了更大的灵活性。在最简单的层次上,像在 DTD 中那样,您可以把属性和元素声明关联起来,指明能够出现的给定元素集合序列:只能出现 1 次(1)、出现 0 次或多次(*)或者出现 1 次或多次(+)。您还可以表示 XML Schema 中的其他约束,比方说使用 element
元素的 minOccurs
和 maxOccurs
属性,以及 choice
、 group
和 all
元素。
<element name='Title' type='string'/> <element name='Author' type='string'/> <element name='Book'> <complexType> <element ref='Title' minOccurs='0'/> <element ref='Author' maxOccurs='2'/> </complexType> </element> |
在 清单 11中, Book
中 Title
的出现是可选的(类似 DTD 的 '?')。但是, 清单 11也说明 Book
元素中至少要有一个但不能超过两个作者。 element
的 minOccurs
和 maxOccurs
属性的默认值是 1。元素 choice
只允许它的一个子女出现在实例中。另外一个元素 all
,表示这样的约束:组中的所有子元素可以同时出现一次,或者都不出现,它们可以按任意的顺序出现。 清单 12表示 Title
和 Author
两者必须同时出现(顺序任意)在 Book
中,或者都不出现。这种约束很难在 DTD 中表示。
<xsd:element name='Title' type='string'/> <xsd:element name='Author' type='string'/> <xsd:element name='Book'> <xsd:complexType> <xsd:all> <xsd:element ref='Tile'/> <xsd:element ref='Author'/> </xsd:all> </xsd:complexType> </xsd:element> |
|
我们已经讨论了在 XML Schema 中定义元素所需的最基本的概念,通过一些简单的例子使您领略到它的强大功能。还有一些更强大的机制:
- XML Schema 对类型继承提供了广泛的支持,允许重用以前定义的结构。使用所谓的 facets,您可以派生新的类型,表示其他某个类型值的更小子集,比如通过枚举、范围或模式匹配来定义子集。在本文的例子中,
ProductCode
类型就是使用模式面(pattern
facet)定义的。子类型也可以向基类型增加更多的元素和属性声明。 - 有几种机制控制能否定义子类型,或者能否在具体的文档中替换为子类型。比如,有可能表示
InvoiceType
( Invoice 编号的类型)不允许子类型化,任何人都不能定义新版本的InvoiceType
。通过规定在特定的上下文中不能用ProductCode
类型的子类型替换,也能表达这种约束。 - 除了子类型外,还可以定义等价的类型,这样,一个类型的值可以用另一个类型代替。
- 通过声明抽象的元素或者类型,XML Schema 提供了一种强制替换机制。
- 为了方便起见,可以定义并命名属性组和元素组,从而能够在后面引用这些组达到重用的目的。
- XML Schema 提供了三个元素——
appInfo
、documentation
和annotation
——为模式作注解,以方便读者(documentation
)和应用程序(appInfo
)。 - 基于子元素的某些属性可以表示惟一性约束。
可以通过 W3C 站点(请参阅 参考资料)的文档进一步研究 XML Schema,或者访问 dW XML 专区了解更多的内容。目前,XML Schema 规范已经被批准,并成为候选推荐标准(Candidate Recommendation),毫无疑问您将越来越多地用到它。
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
- 通过填写 Web 表单,可以免费获得爱丁堡大学/W3C 的 XSV(alpha 版),这是一个 XML Schema Validator。
- 免费下载 Apache 项目的 XML Schema validator。
- Extensibility 的商业软件 XML Schema aware editor/validator的评估版。
- 从 http://www.ibm.com/alphaworks下载免费工具。
- 最新 XML Schema 规范的背景资料: XML Schema Part 0: Primer 。
- 目前的 W3C XML Schema 规范文档由两部组成: XML Schema Part 1: Structures 和 XML Schema Part 2: Datatypes 。
|
Ashvin Radiya 是 AvantSoft, Inc.的创始人和总裁。作为首席技术官,他领导开发并推出了最新 Java 编程及相关技术的 AvantSoft 培训课程。他还建立和管理与财富 100 强的战略伙伴关系。Ashvin 有丰富的企业、学院和专业人员经历。他曾在奥斯汀的 IBM 工作,研究基于 CORBA 的高级分布式面向对象产品。在移动商务、XML、Java、Enterprise JavaBeans 组件、InfoBus、安全、CORBA 和分布式面向对象编程方面,他拥有丰富的知识和经验。Ashvin 从锡拉丘兹大学获得了计算机科学博士学位。可以通过 mailto:ashvin@avantsoft.com?cc=与 Ashvin Radiya 联系。 |
|
Vibha Dixit 在 AvantSoftInc. 的 Business Development Manager and Technologist 中担任重要职务。她负责业务规划、管理战略伙伴、开发新的客户、营销和市场。她还积极参与 AvantSoft 技术目标的制定,确定移动商务、XML 和 Java 技术领域的发展方向。Vibha 在商业管理方面有独到的经验,在计算机技术方面也有丰富的行业经验。在加入 AvantSoft 之前,她曾在 IBM Santa Teresa 实验室工作,从事分布式交互对象中间件的研究。在俄亥俄超级计算机中心,她参与了一个微型计算机操作系统的设计与开发。Vibha 从俄亥俄州立大学获得了计算机科学博士学位。她还从 Southern Methodist University 修完了 MBA 高级管理人员课程。可以通过 mailto:vibha@avantsoft.com?cc=与 Vibha Dixit 联系。 |