瞭解 XML 架構
發佈日期 :
Aaron Skonnard
DevelopMentor
2003 年 3 月
適用於:
類型系統
XML 架構定義語言 (XSD)
Web 服務開發
本頁內容
摘要:XML 架構預計將在未來的 XML 處理中扮演核心角色,尤其是在 Web 服務領域,它將作爲構建更高級別抽象的重要基礎之一。本文詳細地說明了如何使用 XML 架構定義語言。(22 頁打印頁)
簡介
1 + 2 = ?
在軟件中,回答此類問題所需的信息是由類型系統來提供的。編程語言使用類型系統來簡化生成優質代碼的任務。類型系統定義了一組可供開發人員在其程序設計中選擇使用的類型和操作。一個類型定義一個值空間,或者換句話說,定義一組可能的值。例如,如果上面的操作數被認爲是數值類型,答案就可能是 3;但如果它們被認爲是字符串型,答案就可能是 “12”,具體情況取決於 “+” 運算符是如何定義的。
類型系統的主要好處之一是,編譯器可以使用它在運行前確定代碼中是否包含錯誤,這樣就避免了可能產生大量的錯誤。編譯器還可以利用類型系統信息針對給定類型生成操作代碼。另外,編譯器和運行庫都在很大程度上依賴類型系統來確定在使用某個特定類型時如何分配內存空間,這使得開發人員可以不關注這些單調乏味的工作。
許多語言和運行庫還允許在運行時以編程方式檢查類型信息。這就使開發人員能夠考慮得多一點,提出關於類型特徵的問題,並且做出基於相應答案的決定。這種在運行時檢查類型信息的技術通常被稱爲反射。在今天的主流編程環境(例如,Microsoft? .NET 框架和 Java)中,反射扮演了重要角色,這有效地減少了開發人員在其代碼中必須考慮的問題。在這些編程環境中,虛擬機(例如,公共語言運行庫或 JVM)提供大多數程序所需的額外服務(例如,安全、垃圾回收、序列化、遠程方法調用甚至是 Web 服務集成)。
圖 1. 類型信息的好處
一個定義完善的類型系統以及反射還能夠創建更好的工具,以便與這種語言共同使用。開發人員已經能夠快速適應許多事情,例如,Microsoft? Intellisense?、代碼完成以及那些能夠大大加速開發過程的方便的紅色 Squiggle。大體說來,一個好的類型系統會提供許多有趣的好處(請參見圖 1),其中的大部分好處是容易被當作理所當然、而沒有時卻讓人倍感失落的那種。
XML 1.0 是一個缺乏智能類型系統的語言的典型示例。如果沒有類型系統,則在 XML 1.0 文檔中找到的信息只能被視爲文本。這就要求開發人員事先知道“真正的類型”,以便他們在代碼中執行必要的強制。
XML 架構定義語言 (XSD) 爲 XML 處理環境提供了一種類型系統。在小容器中,XML 架構可以描述您要使用的類型。符合 XML 架構類型的 XML 文檔通常是指實例 文檔,這與類和對象間傳統的面向對象的 (OO) 關係非常相似(請參見圖 2)。這是一種跳離文檔類型定義 (DTD) 的基本工作方式的概念切換,它可在映射到傳統的編程語言或數據庫類型系統時提供更大的靈活性。在這些環境中,XML 架構大大否決了 DTD 的使用。
圖 2. OO 與 XML 概念
XML 架構只有在一種完全以 XML 爲中心的方式下,才能夠提供圖 1 所示的全部好處。包含 XML 架構類型信息的邏輯 XML 文檔通常被稱爲後架構驗證信息集 (PSVI)。PSVI 使得如下操作成爲可能:像在其他編程環境中一樣,在運行時執行基於 XML 架構的反射。總的說來,XML 架構預計將在未來的 XML 處理中扮演核心角色,尤其是在 Web 服務領域,它將作爲構建更高級別抽象的重要基礎之一。本文的剩餘部分將更詳細地介紹如何使用 XML 架構定義語言。
數據類型:值和詞法空間
XML 架構提供了一個內置數據類型 清單,開發人員可以使用它來約束文本(有關幫助圖,請參見 W3C XML Schema Part 2:Datatypes Web page)。所有這些類型都可以在 http://www.w3.org/2001/XMLSchema 命名空間中找到。每種類型都有一個定義好的值空間。類型的值空間僅僅是可用在給定類型的實例中的一組值。
圖 3. 字節值空間
例如,XML 架構提供了一種名爲字節的內置類型,它具有從 -128 到 127 的值空間。另一個示例是 XML 架構中的布爾 類型,它的值空間非常簡單,因爲它只有以下兩個值:真 和假。共有 44 種內置類型供您選擇,每種都有不同的值空間以滿足不同數據建模的需要。
圖 4 闡釋了許多內置類型都被定義爲另外一種類型的值空間的子集,也稱爲通過限制派生。例如,字節型值空間是短整型值空間的子集,短整型值空間又是整型值空間的子集,而整型值空間又是長整型值空間的子集,等等。因此,基本集合論告訴我們,一種派生類型的實例也是它的任一祖先類型的有效實例。(嚴格地說,它們是 anySimpleType 本身的子集。)
儘管編程語言使用值空間信息來計算需要多大的內存來表示值,開發人員卻極少需要擔心將它們表示爲文本的問題。然而,對於 XML,卻不能忽視一個事實,那就是實例將很可能序列化爲 XML 1.0 文件,這需要以詞法形式表示值。如果每個 XML 架構處理器都獨立地決定如何進行此操作,那麼互操作性很快就會失去。因此,除了定義每種類型的值空間外,XML 架構還定義了它們所允許的詞法表示形式(詞法空間)。
圖 4. 類型子集
例如,布爾型的真值可以表示爲 “true” 或 “1”,而布爾型的假值可以表示爲 “false” 或 “0”。雙精度型值 10 可以表示爲 “10”、“10.0” 或 “10.0000”,甚至可表示爲 “0.01E
在命名空間中定義類型
除了提供內置類型外,大部分編程語言還允許開發人員定義他們自己的類型,它們通常被稱爲用戶定義類型 (UDT)。在定義 UDT 時,大部分編程語言還允許您用命名空間來限定它們,以便使它們不會與其他恰好與其具有相同名稱的 UDT 相混淆。有關 XML 命名空間如何工作的詳細信息,請參閱瞭解 XML 命名空間。圖 5 顯示了一個 C# 命名空間定義和一個與之類似的 XML 架構定義。正如您所看到的一樣,XML 架構還支持在命名空間內定義類型。
圖 5. 在命名空間中定義類型
xsd:schema 元素確定命名空間中的內容範圍,而targetNamespace 屬性指定命名空間的名稱。例如,下面的 XML 架構模板定義一個新的名爲 http://example.org/publishing 的命名空間:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.org/publishing"
xmlns:tns="http://example.org/publishing"
>
<!-- type definitions -->
<xsd:simpleType name="AuthorId">
<!-- define value space details here -->
...
</xsd:simpleType>
<xsd:complexType name="AuthorType">
<!-- define structural details here -->
...
</xsd:complexType>
<!-- global element/attribute declarations -->
<xsd:element name="author" type="tns:AuthorType"/>
<xsd:attribute name="authorId" type="tns:AuthorId"/>
...
</xsd:schema>
位於 xsd:schema 元素內的所有內容(作爲直接子級)都被認爲是全局的,因此它們會自動與目標命名空間相關聯。在上例中,http://example.org/publishing 命名空間中共有 4 個元素:AuthorId、AuthorType、author 和 authorId。因此,無論何時在架構內引用其中的一個元素,都必須使用命名空間限定的名稱。
爲了使用命名空間限定的名稱,還將需要另外一個命名空間聲明,該聲明映射到架構的 targetNamespace 值。上面顯示的 “tns” 命名空間聲明的作用就在於此。因此,我無論何時需要引用我在架構中定義的內容,都可以在名稱前加上 “tns” 前綴,如本例所示。
您可以在 xsd:schema 元素內定義兩種類型:簡單類型(使用 xsd:simpleType)和複雜類型(使用 xsd:complexType)。簡單類型只能分配給純文本元素和屬性,因爲它們並不定義結構,而是定義值空間。具有附加結構的元素(例如,帶有屬性或子元素的元素)必須定義爲複雜類型。
除了定義類型,您還可以在架構內定義全局元素(使用 xsd:element)和屬性(使用 xsd:attribute),併爲它們指定類型。在上例中,我定義了一個名爲 author 的全局元素和一個名爲 authorId 的全局屬性。因爲這些構造也是全局的,所以當我在實例文檔中使用它們時,必須通過目標命名空間對其進行限定。下面的 XML 文檔包含前面定義的 author 元素的一個實例:
<x:author xmlns:x="http://example.org/publishing">
<!-- structure determined by complexType definition -->
...
</x:author>
下面的 XML 文檔包含全局 authorId 屬性:
<!-- authorId value constrained by simpleType definition -->
<publication xmlns:x="http://example.org/publishing"
x:authorId="333-33-3333"/>
也可以使用 http://www.w3.org/2001/XMLSchema-instance 命名空間中的 type 屬性爲實例文檔中的元素顯式指定類型。這個命名空間包含少數只能用在實例文檔中的屬性。使用類型屬性類似於在一些編程語言中的類型間進行強制轉換。下例爲 genericId 元素(尚未在架構中定義)顯式指定 AuthorId 類型:
<genericId
xmlns:x="http://example.org/publishing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="tns:AuthorId"
>333-33-3333</genericId>
請注意,AuthorId 和我們指定給上面顯示的全局 authorId 屬性的類型是相同的。這表明您能夠爲屬性或純文本元素指定簡單類型以約束它們的值。同樣,一定要注意到,用於指定類型的 xsi:type 技術只能應用於元素,而不能應用於屬性。
定義簡單類型
大多數編程語言只允許開發人員將多種內置類型排列爲某種結構化類型,而不允許開發人員定義新的具有用戶定義的值空間的簡單類型。在這一點上,XML 架構有所不同,因爲它允許用戶定義其各自的自定義簡單類型,這些簡單類型的值空間是預定義的內置類型的子集。
像前面顯示的那樣,您可以使用 xsd:simpleType 元素定義新的簡單類型。在 xsd:simpleType 元素內,可以指定一個您希望限制(使用 xsd:restriction 元素)其值空間的基類型。在 xsd:restriction 元素內,您可以通過限制一個或多個方面 來準確指定希望如何來限制基類型。例如,下面的簡單類型使用 xsd:minInclusive 和 xsd:maxInclusive 方面約束 xsd:double 和 xsd:date 值空間:
...
<xsd:simpleType name="RoyaltyRate">
<xsd:restriction base="xsd:double">
<xsd:minInclusive value="0"/>
<xsd:maxInclusive value="100"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="Pubs2003">
<xsd:restriction base="xsd:date">
<xsd:minInclusive value="
<xsd:maxInclusive value="
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="rate" type="tns:RoyaltyRate"/>
<xsd:element name="publicationDate" type="tns:Pubs2003"/>
...
下面的文檔包含上面定義的元素的有效實例:
<x:rate xmlns:x="http://example.org/publishing">17.5</x:rate>
<x:publicationDate xmlns:x="http://example.org/publishing"
>
XML 架構定義了可用於每種類型的方面(請參閱表 1)。大多數方面都不能應用於所有類型(一些方面僅對某些類型有意義)。大多數方面限制了類型的值空間,而模式方面則限制了類型的詞法空間。對於值空間和詞法空間來說,限制兩者中的任一者都會間接地限制另外一個。先前的示例約束了基類型的值空間,而接下來的示例使用正則表達約束了字符串的詞法空間:
...
<xsd:simpleType name="SSN">
<xsd:restriction base="xsd:string">
<xsd:pattern value="/d{3}-/d{2}-/d{4}"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="PublisherAssignedId">
<xsd:restriction base="xsd:string">
<xsd:pattern value="/d{2}-/d{8}"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="Phone">
<xsd:restriction base="xsd:string">
<xsd:pattern value="/(/d{3}/)/d{3}-/d{4}"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="authorId" type="tns:SSN"/>
<xsd:element name="pubsAuId" type="tns:PublisherAssignedId"/>
<xsd:element name="phone" type="tns:Phone"/>
...
下面的文檔包含上面定義的元素的有效實例:
<x:authorId xmlns:x="http://example.org/publishing"
>123-45-6789</x:authorId>
<x:pubsAuId xmlns:x="http://example.org/publishing"
>01-23456789</x:pubsAuId>
<x:phone xmlns:x="http://example.org/publishing"
>(801)390-4552</x:phone>
只有與正則表達式(在模式方面中指定)相匹配的字符串才被認爲是給定類型的有效實例。
方面元素 |
說明 |
xsd:enumeration |
指定一個該類型必須匹配的固定值。 |
xsd:fractionDigits |
指定小數點右側十進制位數的最大值。 |
xsd:length |
指定基於字符串的類型中的字符數量、基於二進制的類型中的八位字節數量或者基於列表的類型中的項數量。 |
xsd:maxExclusive |
指定該類型的值空間的上限(不包括上限)。 |
xsd:maxInclusive |
指定該類型的值空間的上限(包括上限)。 |
xsd:maxLength |
指定基於字符串的類型中字符的最大數量、基於二進制的類型中的八位字節的最大數量或者基於列表的類型中項的最大數量。 |
xsd:minExclusive |
指定該類型的值空間的下限(不包括下限)。 |
xsd:minInclusive |
指定該類型的值空間的下限(包括下限)。 |
xsd:minLength |
指定基於字符串的類型中字符的最小數量、基於二進制的類型中的八位字節的最小數量或者基於列表的類型中項的最小數量。 |
xsd:pattern |
基於正則表達式指定一個該類型必須匹配的模式。 |
xsd:totalDigits |
爲從數字派生的類型指定十進制位數的最大值。 |
xsd:whiteSpace |
指定空白正常化規則。 |
|
表 1. 方面
另一個有趣的方面是 xsd:enumeration,它允許將值空間約束爲枚舉值列表。下面的示例將 xsd:NMTOKEN 的值空間約束爲四個特定的枚舉值:
...
<xsd:simpleType name="PublicationType">
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="Book"/>
<xsd:enumeration value="Magazine"/>
<xsd:enumeration value="Journal"/>
<xsd:enumeration value="Online"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="pubType" type="tns:PublicationType"/>
...
下面的文檔包含上面定義的元素的有效實例:
<x:pubType xmlns:x="http://example.org/publishing"
>Online</x:pubType>
派生元素 |
說明 |
xsd:restriction |
新類型是現有類型的限制,這表示新類型具有一組範圍更窄的合法值。 |
xsd:list |
新類型是另一個簡單類型的、用空白分隔的列表。 |
xsd:union |
新類型是兩個或更多其他簡單類型的聯合。 |
|
表 2. 簡單類型的構造技巧
除了限制類型的值空間外,還可以構造新的作爲其他簡單類型的列表 或聯合 的簡單類型。爲此,要使用 xsd:list 或 xsd:union 元素,而不使用 xsd:restriction(請參閱表 2)。在使用 xsd:list 時,實質上是在從指定的值空間定義一個用空白分隔的值列表。值得提醒的是,在使用 xsd:list 或 xsd:union 時,不像使用 xsd:restriction 時那樣具有派生層次結構,因此在這些情況下不能應用類型兼容性。下例將名爲 AuthorList 的新類型定義爲 SSN 值列表。
...
<xsd:simpleType name="AuthorList">
<xsd:list itemType="tns:SSN"/>
</xsd:simpleType>
<xsd:element name="authors" type="tns:AuthorList"/>
...
下面的文檔包含 authors 元素的有效實例:
<x:authors xmlns:x="http://example.org/publishing"
>111-11-1111 222-22-2222 333-33-3333 444-44-4444</x:authors>
對於 xsd:union 來說,是在創建一種可將多個值空間組合到一個新的值空間中的新類型。聯合類型的實例可以是所指定的任何值空間中的值。例如,下面的名爲 AuthorId 的類型將 SSN 值空間與 PublisherAssignedId 值空間組合在一起:
...
<xsd:simpleType name="AuthorId">
<xsd:union memberTypes="tns:SSN tns:PublisherAssignedId"/>
</xsd:simpleType>
<xsd:element name="authorId" type="tns:AuthorId"/>
...
下面的每個文檔都顯示 authorId 元素的一個有效實例:
<x:authorId xmlns:x="http://example.org/publishing"
>111-11-1111</x:authorId>
<x:authorId xmlns:x="http://example.org/publishing"
>22-22222222</x:authorId>
XML 架構對用戶定義類型(以及更具體的自定義值空間/詞法空間)的支持是這種語言更強大的方面之一。由於大多數編程語言不提供該支持,因此開發人員不得不在他們的應用程序代碼中處理此類問題(通常是通過屬性的 setter)。這種可定義能夠完全滿足您的需求的自定義值空間/詞法空間的功能使錯誤處理和驗證代碼問題降低一個難度級別。
...
<xsd:complexType name="AuthorType">
<!-- compositor goes here -->
</xsd:complexType>
...
定義複雜類型
XML 架構允許將不同的簡單類型(或值空間)排列爲結構(也稱作複雜類型)。可以使用 xsd:complexType 元素在架構的目標命名空間內定義新的複雜類型,如下所示:
xsd:complexType 元素包含所謂的合成器,合成器描述類型內容的合成,因此又被稱作該元素的內容模型。XML 架構定義了三個可用在複雜類型定義中的合成器:xsd:sequence、xsd:choice 和 xsd:all(請參閱表 3)。
合成器中包含粒子,而粒子中包括諸如其他合成器、元素聲明、通配符和模型組之類的內容。屬性聲明並不被視爲粒子,因爲它們不重複。因此,屬性聲明不會放在合成器內,而是放在複雜類型定義結尾處的合成器後面。
合成器 |
定義 |
xsd:sequence |
所包含粒子的有序序列。 |
xsd:choice |
可供選擇的所包含粒子。 |
xsd:all |
以任何順序排列的所有所包含粒子。 |
|
表 3. 複雜類型合成器
元素聲明 (xsd:element) 可能是最常用的粒子。下面的名爲 AuthorType 的 complexType 定義了一個由兩個子元素和一個屬性(此處的子元素和屬性分屬於不同的簡單類型)組成的有序序列:
...
<xsd:complexType name="AuthorType">
<!-- compositor goes here -->
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="phone" type="tns:Phone"/>
</xsd:sequence>
<xsd:attribute name="id" type="tns:AuthorId"/>
</xsd:complexType>
<xsd:element name="author" type="tns:AuthorType"/>
...
在 xsd:complexType 元素內聲明的元素和屬性被視爲該複雜類型的局部 元素和屬性。局部元素和屬性只能在定義它們的上下文內使用。這就引發了一個有趣的問題:在實例文檔中,局部元素/屬性是否需要由命名空間來限定。因爲局部元素和屬性總是包含由目標命名空間限定的祖先元素(通常是全局元素),所以人們可以認爲讓局部元素和屬性也由命名空間限定並不是必要的。這與大多數編程語言中的工作方式相類似 - 如果您在一個命名空間中定義了一個類,則只有該類的名稱由命名空間限定,而它的局部成員則不會被限定。
由於這個原因,在 XML 架構中,局部元素和屬性在默認情況下應當不受限定。因此,author 元素的有效實例如下所示:
<x:author xmlns:x="http://example.org/publishing"
id="333-33-3333"
>
<name>Aaron Skonnard</name>
<phone>(801)390-4552</phone>
</x:author>
然而,XML 架構允許使用 xsd:element/xsd:attribute 的 form 屬性或者使用 xsd:schema 的 elementFormDefault/attributeFormDefault 屬性,來顯示控制給定的局部元素/屬性是應當受限定還是不受限定,如下所示:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.org/publishing"
xmlns:tns="http://example.org/publishing"
elementFormDefault="qualified"
attributeFormDefault="qualified"
>
...
</xsd:schema>
在有了此架構之後,下面的實例將被視爲有效實例(而上面的實例將被視爲無效實例):
<x:author xmlns:x="http://example.org/publishing"
x:id="333-33-3333"
>
<x:name>Aaron Skonnard</x:name>
<x:phone>(801)390-4552</x:phone>
</x:author>
在大多數情況下,只要實例與架構相符合,那麼對局部元素使用哪種命名空間樣式就無關緊要了。
您也可以使用 ref 屬性在一個複雜類型內引用全局元素/屬性聲明,如下所示:
...
<!-- global definitions -->
<xsd:attribute name="id" type="tns:AuthorId"/>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="author" type="tns:AuthorType"/>
<xsd:complexType name="AuthorType">
<!-- compositor goes here -->
<xsd:sequence>
<!-- reference to global element -->
<xsd:element ref="tns:name"/>
<xsd:element name="phone" type="tns:Phone"/>
</xsd:sequence>
<!-- reference to global attribute -->
<xsd:attribute ref="tns:id"/>
</xsd:complexType>
...
由於 id 和 name 是全局元素,因此它們在實例文檔中總是需要受到限定。使用 “ref” 可以指定全局元素同樣可以用在 AuthorType μ?上下文內,但這並沒有改變它需要受到限定這一事實。phone 元素仍然是在局部定義的,這意味着,在實例中,它可能需要也可能不需要受到限定,具體情況取決於所使用的形式。因此,假設
elementFormDefault="unqualified"
,有效實例將如下所示:
<x:author xmlns:x="http://example.org/publishing"
x:id="333-33-3333"
>
<x:name>Aaron Skonnard</x:name>
<phone>(801)390-4552</phone>
</x:author>
下面是一個稍微複雜點的示例,它使用嵌套的複雜類型、其他合成器和重複粒子:
<xsd:complexType name="AddressType">
<xsd:all>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string" minOccurs="0"/>
<xsd:element name="state" type="tns:State" minOccurs="0"/>
<xsd:element name="zip" type="tns:Zip"/>
</xsd:all>
</xsd:complexType>
<xsd:complexType name="PublicationsListType">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="book" type="xsd:string"/>
<xsd:element name="article" type="xsd:string"/>
<xsd:element name="whitepaper" type="xsd:string"/>
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="AuthorType">
<xsd:sequence>
<xsd:choice>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="fullName" type="xsd:string"/>
</xsd:choice>
<xsd:element name="address" type="tns:AddressType"/>
<xsd:element name="phone" type="tns:Phone"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="recentPublications"
type="tns:PublicationsListType"/>
</xsd:sequence>
<xsd:attribute name="id" type="tns:AuthorId"/>
</xsd:complexType>
<xsd:element name="author" type="tns:AuthorType"/>
...
在本例中,AuthorType 包含由另一個合成器和一個選項組成的序列,其後還有三個元素聲明。一些元素屬於其他由用戶定義的複雜類型(AddressType 和 PublicationsListType),這些複雜類型可在該類型內有效地定義嵌套結構。此選項意味着是允許 name 元素還是允許 fullName 元素出現在該位置。最後,AddressType 中的 all 合成器指出元素的順序是可以忽略的。
另請注意,phone 元素聲明使用 minOccurs 和 maxOccurs 元素指定出現約束。出現約束可以應用於複雜類型中的任何粒子。每個出現約束的默認值是 1,這意味着給定的粒子必須在指定的位置出現一次。指定
minOccurs="0"
會使給定的粒子成爲可選粒子,而指定
maxOccurs="unbounded"
會允許粒子重複無限多次。您也可以根據自己的喜好指定任意限制,例如,
minOccurs="3" maxOccurs="77"
。總的來說,針對合成器使用出現約束是適用於整個組的(請注意,PublicationsListType 將出現約束應用於某個選項)。下面舉例說明新的 AuthorType 的有效實例:
<x:author xmlns:x="http://example.org/publishing"
id="333-33-3333"
>
<name>Aaron Skonnard</name>
<address>
<street>123
<zip>84043</zip>
</address>
<phone>801-729-0924</phone>
<phone>801-390-4555</phone>
<phone>801-825-3925</phone>
<recentPublications>
<whitepaper>Web Service Abstractions</whitepaper>
<book>Essential XML Quick Reference</book>
<article>Web Services and DataSets</article>
<article>Understanding SOAP</article>
<book>Essential XML</book>
</recentPublications>
</x:author>
在默認情況下,複雜類型具有閉合式內容模型。這就意味着只允許指定的粒子出現在實例中。然而,XML 架構允許使用所謂的通配符 定義開放式內容模型。在複雜類型內使用 xsd:any 意味着任何元素都可以出現在那個位置上,這可有效地使它成爲事先無法預知的內容的佔位符。還可以使用 xsd:anyAttribute 來爲屬性定義佔位符。
<xsd:complexType name="AuthorType">
<!-- compositor goes here -->
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="phone" type="tns:Phone"/>
<xsd:any minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:anyAttribute/>
</xsd:complexType>
<xsd:element name="author" type="tns:AuthorType"/>
下面闡釋了上面定義的 author 元素的一個有效實例:
<x:author xmlns:x="http://example.org/publishing"
xmlns:aw="http://www.aw.com/legal/contracts"
aw:auId="01-3424383"
>
<!-- explicitly defined by the complexType -->
<name>Aaron Skonnard</name>
<phone>801-825-3925</phone>
<!-- extra elements that replace wildcard -->
<aw:contract xmlns:aw="http://www.aw.com/legal/contracts">
<title>Essential Web Services Quick Reference</title>
<deadline>
</aw:contract>
...
</x:author>
在使用通配符時,也可以約束內容實際所來自的命名空間。xsd:any 和 xsd:anyAttribute 都附帶了一個可選的命名空間屬性,該屬性可以包含表 4 中所示的任一個值。這使得有關通配符替換來自何處的內容變得非常具體。
屬性值 |
允許使用的元素 |
##any |
來自任何命名空間的任何元素 |
##other |
非 targetNamespace 命名空間中的任何元素 |
##targetNamespace |
targetNamespace 中的任何元素 |
##local |
任何非限定元素(不屬於命名空間) |
ns 字符串列表 |
來自列出的命名空間中的任何元素 |
|
表 4. 通配符的命名空間屬性
使用通配符,也可以指定在驗證過程中架構處理器應該如何處理通配符內容。xsd:any 和 xsd:anyAttribute 都附帶了一個 processContents 屬性,該屬性可以指定下列三個值之一:lax、strict 和 skip。這個值告訴處理器是否應當針對替換通配符的內容執行架構驗證。Strict 指示處理器必須 針對內容執行驗證,Lax 指示處理器應當 在架構信息可用時執行驗證,而 skip 指示處理器不能 執行架構驗證。
讓我們看一個使用這些屬性的示例。SOAP 1.1 的架構實際上使用通配符以及這兩個屬性來定義 soap:Header 和 soap:Body 元素的結構:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/"
targetNamespace="http://schemas.xmlsoap.org/soap/envelope/"
>
...
<xs:element name="Header" type="tns:Header" />
<xs:complexType name="Header" >
<xs:sequence>
<xs:any namespace="##other" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##other"
processContents="lax" />
</xs:complexType>
<xs:element name="Body" type="tns:Body" />
<xs:complexType name="Body" >
<xs:sequence>
<xs:any namespace="##any" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##any"
processContents="lax" />
</xs:complexType>
...
</xs:schema>
根據該架構,soap:Header 可以包含零個或更多元素,以及來自非 targetNamespace 命名空間中的任意數量的屬性,而 soap:Body 可以包含零個或更多元素,以及來自任意命名空間中的任意數量的屬性。在這兩種情況下,只有當架構信息在運行時可用時,才應當執行驗證(例如,lax 驗證)。因爲事先無法預知什麼內容將被置於 soap:Header 或 soap:Body元素中,因此通配符提供了一種爲 XML 消息處理定義靈活的開放式框架的方法。
定位和管理架構
在這一點上總出現的問題之一是,XML 架構處理器在運行時如何定位給定的實例文檔所需的架構定義。XML 架構處理器切斷示例文檔的命名空間以定位相應的架構,但 XML 架構規範並未明確指定處理器應當如何進行此操作。大多數處理器都允許您事先加載一個包含您將要使用的所有架構的架構緩存。然後,在運行時,您只需將處理器指向架構緩存,以便它能夠有效地查找特定實例所需的架構。
XML 架構還定義了一種在實例文檔中提供架構位置提示的方法。這是通過 xsi:schemaLocation 屬性實現的,如下所示:
<x:author xmlns:x="http://example.org/publishing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://example.org/publishing pubs.xsd"
>
xsi:schemaLocation 屬性允許您提供一個由空格分隔的、包含命名空間名稱和 URI 位置對(指出到哪裏去查找特定的架構文件)的列表。但是,這同樣只是一個提示,處理器可能不會在那裏實際查看是否還有一個更高效的檢索機制。
小結
XML 架構爲 XML(能夠提供許多功能強大的服務)提供了一個表達類型系統。我們已經涵蓋了 XML 架構定義的基礎,包括簡單類型定義和複雜類型定義。簡單類型定義允許您爲純文本元素和屬性定義自定義的值空間。另一方面,複雜類型定義允許您將簡單類型排列爲結構。
事實上,XML 架構的功能遠比我們在這裏所討論到的多。例如,複雜類型定義支持通過擴展和限制進行派生,這就允許您以一種很好地映射到 OO 類層次結構的方式定義複雜類型層次結構。在構建好複雜類型層次結構之後,還可以在實例文檔中使用替換技術。XML 架構還使 XML 架構定義能夠貫穿到多個文件和命名空間之中,然後通過其被包括和/或導入,以便增加重用性並簡化維護。這些更高級的話題還是留在將來的關於 XML 架構設計的文章中介紹吧。
有關 XML 架構的更多信息,請查看電子版的 Essential XML Quick Reference(可從網上免費下載)— XML 架構章節包含各個構造和數據類型的簡化說明和示例。
參考