概述 |
迄今为止,您已经在本教程中了解到了 XML 文档的基本规则;那当然很好,但您需要定义将用来表示数据的元素。您将在本章了解做到那一点的两种方法。
- 一种方法是使用文档类型定义(Document Type Definition),或简称 DTD。DTD 定义可以在 XML 文档中出现的元素、这些元素出现的次序、它们可以如何相互嵌套以及 XML 文档结构的其它详细信息。DTD 是最初的 XML 规范的一部分,与 SGML DTD 非常相似。
- 另一种方法是使用 XML Schema。模式可以定义您能在 DTD 中使用的所有文档结构,它还可以定义数据类型和比 DTD 更复杂的规则。W3C 在提出最初的 XML 规范的几年之后开发了 XML Schema 规范。
文档类型定义 |
DTD 允许您指定 XML 文档的基本结构。下面的几页将研究 DTD 片段。首先是定义本章中地址文档示例的基本结构的 DTD, XML 是什么? :
<!--address.dtd -->
<!ELEMENT address (name, street, city, state, postal-code)>
<!ELEMENT name (title? first-name, last-name)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT first-name (#PCDATA)>
<!ELEMENT last-name (#PCDATA)>
<!ELEMENT street (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT postal-code (#PCDATA)>
该 DTD 定义了样本文档中使用的所有元素。它定义了三个元素:
<address>
元素包含一个<name>
、一个<street>
、一个<city>
、一个<state>
和一个<postal-code>
。所有这些元素必须出现,而且必须以这个顺序出现。<name>
元素包含一个可选的<title>
元素(问号表示 title 这个元素是可选的),后面跟有一个<first-name>
和一个<last-name>
元素。- 所有其它包含文本的元素。(
#PCDATA
代表已解析字符数据;不能在这些元素中包含另一个元素。)
尽管 DTD 相当简单,但它清楚地说明了什么样的元素组合是合乎规则的。<postal-code>
元素在 <state>
元素之前的地址文档是不合乎规则的,没有 <last-name>
元素的文档也不合乎规则。
另外,请注意,DTD 语法不同于普通的 XML 语法。(相反,XML Schema 文档本身就是 XML,这导致一些有趣的结果)。尽管 DTD 的语法不同,但您仍可以将普通的注释放到 DTD 中。
DTD 中的符号 |
DTD 中有几个符号用于指出某元素在 XML 文档中可能会出现多少次(或是否出现)。下面是一些示例及其含义:
<!ELEMENT address (name, city, state)>
<address>
元素必须包含一个<name>
元素、一个<city>
元素和一个<state>
元素,并且符合上面的次序。所有的元素都是必需的。逗号表示项的列表。<!ELEMENT name (title?, first-name, last-name)>
这意味着
<name>
元素包含一个可选的<title>
元素,后面必须跟有一个<first-name>
和一个<last-name>
元素。问号表示这一项是可选的;它可以出现一次或根本不出现。<!ELEMENT addressbook (address+)>
<addressbook>
元素包含一个或多个<address>
元素。您可以有任意多的<address>
元素,但必须至少有一个。加号表示这一项必须至少出现一次,但可出现任意次。<!ELEMENT private-addresses (address*)>
<private-addresses>
元素包含零个或多个<address>
元素。星号表示这一项可以出现任意次,包括零次。<!ELEMENT name (title?, first-name, (middle-initial | middle-name)?, last-name)>
<name>
元素包含一个可选的<title>
元素,后面跟有一个<first-name>
元素,随后可能跟有一个<middle-initial>
或<middle-name>
元素,然后跟有一个<last-name>
元素。换句话说,<middle-initial>
和<middle-name>
都是可选的,而且您只能选择二者中的一个。竖线符号表示选择列表;您只能从列表选择一项。还要注意该示例用圆括号对特定元素进行分组,然后在这个组旁放上问号。<!ELEMENT name ((title?, first-name, last-name) | (surname, mothers-name, given-name))>
这个
<name>
元素可以包含两个序列中的一个:一个可选的<title>
,后面跟有一个<first-name>
和一个<last-name>
;或一个<surname>
、一个<mothers-name>
和一个<given-name>
。
灵活性说明 |
在继续之前,有一个关于为了灵活性而设计 XML 文档类型的快速说明。考虑一下前面的样本名称和地址文档类型;显然我是针对美国邮政地址习惯编写了该文档。如果您想要定义其它地址类型的规则的 DTD 或模式,您就必须向它添加更复杂的东西。有一个
<state>
元素在澳大利亚或许有意义,但在英国就毫无意义。或许能够用
文档类型定义
中的样本 DTD 处理加拿大的地址,但添加 <province>
元素会更好。最后,要知道在世界的很多地方,象头衔、名和姓这样的概念没有意义。
要点:如要定义一个 XML 文档的结构,您应该象在应用程序中设计数据库模式或数据结构那样事先考虑 DTD 或模式。您事先考虑的未来需求越多,以后实现它们就越容易而且成本越低。
定义属性 |
本篇介绍性教程没有很详细地研究 DTD 如何工作,但这里要介绍一个更基本的概念:定义属性。您可以定义将在 XML 文档中出现的元素的属性。使用 DTD,您还可以:
- 定义哪些属性是必需的
- 定义属性的缺省值
- 列出给定属性的所有有效值
假设您要更改 DTD,使 state
成为 <city>
元素的属性。下面演示如何做到这一点:
<!ELEMENT city (#PCDATA)> <!ATTLIST city state CDATA #REQUIRED>
和以前一样,我们定义了<city>
元素,但修改的示例还使用了ATTLIST
声明来列出元素的属性。属性列表中的名称city
告诉解析器这些属性是为<city>
元素定义的。名称state
是属性的名称,关键字CDATA
和#REQUIRED
告诉解析器state
属性包含文本并且是必需的(如果它是可选的,用CDATA #IMPLIED
即可)。
要定义元素的多个属性,可象下面那样编写 ATTLIST
:
<!ELEMENT
city (#PCDATA)> <!ATTLIST city state CDATA #REQUIRED postal-code CDATA #REQUIRED>
该示例将 state
和 postal-code
都定义为 <city>
元素的属性。
最后,DTD 允许您定义属性缺省值,并列举属性的所有有效值:
<!ELEMENT
city (#PCDATA)>
<!ATTLIST city state CDATA (AZ|CA|NV|OR|UT|WA) "CA">
上面的示例表明它仅支持来自亚利桑那州(AZ)、加利福尼亚州(CA)、内华达州(NV)、俄勒冈州(OR)、犹他州(UT)和华盛顿州(WA)的地址,并且缺省值是加利福尼亚州。这样,您就可以进行形式非常有限的数据验证。尽管这是有用的功能,但它只是您能用 XML 模式(请参阅XML 模式)完成的工作的一个小子集。
XML 模式 |
使用 XML 模式,您会有更多的能力来定义什么样的 XML 文档是有效的。它们与 DTD 相比有几个优势:
- XML 模式使用 XML 语法。换句话说,XML 模式是一个 XML 文档。这意味着您可以象处理任何其它文档一样处理模式。例如,您可以编写一个 XSLT 样式表,该样式表将 XML 模式转换成具有自动生成的 JavaScript 代码的 Web 表单,其中的 JavaScript 代码可以验证您输入的数据。
- XML 模式支持数据类型。尽管 DTD 确实支持数据类型,但很明显这些数据类型是从发布的角度开发的。XML 模式支持 DTD 中的所有原始数据类型(诸如标识和标识引用之类的类型)。它们还支持整数、浮点数、日期、时间、字符串、URL 和其它对数据处理和验证有用的数据类型。
- XML 模式是可扩展的。除了 XML 模式规范中定义的数据类型以外,您还可以创建自己的数据类型,并且可以基于其它数据类型派生出新的数据类型。
- XML 模式有更强的表达能力。例如,您可以用 XML 模式定义任何
<state>
属性值不得超过 2 个字符,或定义任何<postal-code>
元素的值必须与正则表达式[0-9]{5}(-[0-9]{4})?
相匹配。您无法用 DTD 做这些事。
样本 XML 模式 |
下面是与原始名称和地址 DTD 相匹配的 XML 模式。它增加了两个约束:
<state>
元素的值必须刚好是两个字符长,<postal-code>
元素的值必须与正则表达式 [0-9]{5}(-[0-9]{4})?
相匹配。尽管这个模式比 DTD 长很多,但它更清楚地表达了有效的文档看起来是什么样子。下面是模式:
<?xml
version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="address">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="name"/>
<xsd:element ref="street"/>
<xsd:element ref="city"/>
<xsd:element ref="state"/>
<xsd:element ref="postal-code"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="name">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="title" minOccurs="0"/>
<xsd:element ref="first-Name"/>
<xsd:element ref="last-Name"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="first-Name" type="xsd:string"/>
<xsd:element name="last-Name" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="state">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:length value="2"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="postal-code">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:pattern value="[0-9]{5}(-[0-9]{4})?"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:schema>
定义模式中的元素
样本 XML 模式中的 XML 模式用 <xsd:element>
元素定义了一些
XML 元素。头两个定义的元素(<address>
和 <name>
)由其它元素组成。<xsd:sequence>
元素定义了包含在这两个元素中的元素的序列。下面是示例:
<xsd:element
name="address">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="name"/>
<xsd:element ref="street"/>
<xsd:element ref="city"/>
<xsd:element ref="state"/>
<xsd:element ref="postal-code"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
与 DTD 版本一样,XML 模式示例定义 <address>
包含一个 <name>
、一个 <street>
、一个 <city>
、一个 <state>
和一个 <postal-code>
元素,并且依照上面的次序。请注意,该模式实际上用 <xsd:complexType>
元素定义了新的数据类型。
大多数元素包含文本;定义它们很简单。您只要声明新的元素,然后指定它的数据类型为 xsd:string
:
<xsd:element
name="title" type="xsd:string"/>
<xsd:element name="first-Name" type="xsd:string"/>
<xsd:element name="last-Name" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
定义模式中的元素内容
样本模式为两个元素的内容定义了约束:<state>
元素的内容必须是两个字符长,而 <postal-code>
元素的内容必须与正则表达式 [0-9]{5}(-[0-9]{4})?
匹配。下面演示如何做到那一点:
<xsd:element
name="state">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:length value="2"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="postal-code">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:pattern value="[0-9]{5}(-[0-9]{4})?"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
对于 <state>
和 <postal-code>
元素,模式用约束定义了新的数据类型。第一个情况使用 <xsd:length>
元素,而第二个情况则使用 <xsd:pattern>
元素定义该元素必须匹配的正则表达式。
这里概要的介绍仅仅触及 XML 模式能力的表面;有整本的书籍讨论这个主题。就这篇介绍性文章而言,我们足以说明 XML 模式是描述什么样的 XML 文档才算有效的非常强大和灵活的方式。