XML 架構(gòu)快速指南
發(fā)布日期: 4/1/2004 | 更新日期: 4/1/2004
Aaron Skonnard
XML 文件存檔下載本文的代碼:
XML0204.exe (35KB)在 所有的 XML 技術(shù)中,XML 架構(gòu)對(duì)軟件開(kāi)發(fā)人員最具價(jià)值,因?yàn)槭撬罱K使在 XML 文檔中加入類型信息成為可能。本專欄是介紹 XML 架構(gòu)基礎(chǔ)知識(shí)的兩部分系列文章中的第一部分。
首先,我們來(lái)回顧一下 XML 架構(gòu)之前的知識(shí)。XML 1.0 規(guī)范附帶了一個(gè)描述 XML 詞匯的內(nèi)置語(yǔ)法,稱為文檔類型定義 (DTD)。DTD 實(shí)際顧及到 XML 1.0是從前任語(yǔ)言 — 標(biāo)準(zhǔn)通用標(biāo)記語(yǔ)言 (SGML) 那里繼承語(yǔ)法的已經(jīng)有一段時(shí)間了。
DTD 使您能夠描述 XML 文檔的結(jié)構(gòu)。例如,假定要使用以下 XML 詞匯描述員工信息:
<employee id="555-12-3434"><name>Monica</name><hiredate>1997-12-02</hiredate><salary>42000.00</salary></employee>
以下 DTD 描述了本文檔的結(jié)構(gòu):
<!-- employee.dtd --><!ELEMENT employee (name, hiredate, salary)><!ATTLIST employeeid CDATA #REQUIRED><!ELEMENT name (#PCDATA)><!ELEMENT hiredate (#PCDATA)><!ELEMENT salary (#PCDATA)>
然后,該 DTD 能夠通過(guò)一個(gè) DOCTYPE 聲明和原始文檔相關(guān)聯(lián),如下所示:
<!DOCTYPE employee SYSTEM "employee.dtd"><employee id="555-12-3434"><name>Monica</name><hiredate>1997-12-02</hiredate><salary>42000.00</salary></employee>
驗(yàn)證是使用 DTD 的主要優(yōu)勢(shì)。當(dāng) XML 1.0 驗(yàn)證分析器讀取該 XML 1.0 文件時(shí),它也能夠讀取相關(guān)聯(lián)的 DTD 并驗(yàn)證是否符合該定義。使用 DTD 進(jìn)行驗(yàn)證能夠減少您必須置入應(yīng)用程序的錯(cuò)誤處理量。
盡管 DTD 非常適合很多基于 SGML 的電子出版應(yīng)用程序,但當(dāng)應(yīng)用到諸如那些圍繞當(dāng)今 Web 應(yīng)用的現(xiàn)代軟件開(kāi)發(fā)領(lǐng)域時(shí),其局限性很快就顯現(xiàn)出來(lái)了。DTD 的主要限制是 DTD 語(yǔ)法和 XML 不兼容,而且 DTD 不支持命名空間、典型編程語(yǔ)言數(shù)據(jù)類型或定義自定義類型。
由于 DTD 語(yǔ)法本身不是 XML,所以不能使用標(biāo)準(zhǔn)的 XML 工具來(lái)程序化地處理這些定義。大多數(shù) XML 1.0 處理器雖然支持 DTD 驗(yàn)證,但由于語(yǔ)法的復(fù)雜性,它不支持對(duì) DTD中找到的信息進(jìn)行程序化訪問(wèn)。
因?yàn)?DTD 甚至在命名空間存在以前就產(chǎn)生了,所以它們不能很好地協(xié)同工作就不奇怪了。事實(shí)上,使用 DTD 描述可識(shí)別命名空間的文檔就像將一個(gè)方形木栓敲進(jìn)一個(gè)圓孔一樣。有關(guān)如何才能艱難地實(shí)現(xiàn)上述功能的詳細(xì)信息,請(qǐng)查看 2001 年 5 月期的
XML 文件專欄。在該專欄中,我提供了一個(gè)可識(shí)別命名空間的示例 DTD。因此,大多數(shù)開(kāi)發(fā)人員要么選用DTD,要么選用命名空間,但沒(méi)有兩個(gè)同時(shí)選用的。
DTD 也是專門為以文擋為中心的系統(tǒng)而設(shè)計(jì)的,在這 種系統(tǒng)中通常不存在程序化數(shù)據(jù)類型。因此,只存在少數(shù)類型標(biāo)識(shí)符用于描述屬性(參見(jiàn)
圖 1)。這些類型標(biāo)識(shí)符和您過(guò)去在編程語(yǔ)言里慣于使用的大不相同。它們實(shí)際上僅僅是文本的特例 (CDATA)。而且,這些類型不能應(yīng)用于純文本元素,只能應(yīng)用于屬性。
最后,DTD 類型系統(tǒng)是不可擴(kuò)展的。這意味著您不得不使用
圖 1中描述的類型。創(chuàng)建在您的問(wèn)題領(lǐng)域中有意義的自定 義類型不屬于 DTD 問(wèn)題的范疇。在面對(duì) XML 架構(gòu)展現(xiàn)的全新而又令人振奮的未來(lái)時(shí),這些局限足以讓任何 XML 開(kāi)發(fā)人員回避使用 DTD。
XML 架構(gòu)基礎(chǔ)知識(shí)
XML 架構(gòu)本身是一個(gè)用于描述 XML 實(shí)例文檔的 XML 詞匯。我之所以使用“實(shí)例”一詞,是因?yàn)橐粋€(gè)架構(gòu)會(huì)描述一類文檔,這類文檔會(huì)有許多不同的實(shí)例(參見(jiàn)圖 2)。這類似于現(xiàn)在面向?qū)ο笙到y(tǒng)中類和對(duì)象之間的關(guān)系。類相對(duì)于架構(gòu),對(duì)象相對(duì)于 XML 文檔。因此,在使用 XML 架構(gòu)時(shí),您通常要使用不止一個(gè)文檔,還有架構(gòu)以及一個(gè)或多個(gè) XML 實(shí)例文檔。
圖 2 命名空間標(biāo)識(shí)符鏈接
架構(gòu)定義中使用的元素來(lái)自 http://www.w3.org/2001/XMLSchema 命名空間,在本專欄的以下部分,我會(huì)將其綁定到 xsd。以下為基本的架構(gòu)模板:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"targetNamespace="http://example.org/employee/"><!-- definitions go here --></xsd:schema>
架構(gòu)定義必須具有一個(gè)根 xsd:schema元素。各種元素都可能嵌套在 xsd:schema 中,包括但不限于 xsd:element、xsd:attribute 和 xsd:complexType,所有這些我都會(huì)進(jìn)行討論。
架構(gòu)定義是一個(gè) XML 文檔這一事實(shí)解決了 DTD 的首要限制。您可以使用標(biāo)準(zhǔn)的 XML 1.0 工具和服務(wù)(如 DOM、SAX、Xpath 和 XSLT)來(lái)處理架構(gòu)定義文檔。這樣做所固有的簡(jiǎn)單性對(duì)各種架構(gòu)工具造成了沖擊。
XML 架構(gòu)和命名空間
置于 xsd:schema 元素中的定義會(huì)自動(dòng)與 targetNamespace 屬性中指定的命名空間相關(guān)聯(lián)。在前面的示例情況下,架構(gòu)定義將與 http://example.org/employee/ 命名空間相關(guān)聯(lián)。
命名空間標(biāo)識(shí)符是聯(lián)系 XML 文檔和對(duì)應(yīng)的架構(gòu)定義的關(guān)鍵(參見(jiàn) 圖 2 )。例如,以下 XML 實(shí)例文檔包含了位于 http://example.org/employee/ 命名空間的 employee 元素:
<tns:employee xmlns:tns="http://example.org/employee/"/>
該 employee 元素的命名空間與架構(gòu)定義中的 targetNamespace 相同。
為了在處理 employee 元素時(shí)利用該架構(gòu),處理器需要找到正確的架構(gòu)定義。架構(gòu)處理器如何為一個(gè)特定的命名空間找到架構(gòu)定義在規(guī)范中沒(méi)有進(jìn)行定義。然而,大多數(shù)處理器會(huì)允許您加載一個(gè)架構(gòu)的內(nèi)存緩存,它會(huì)在處理文檔時(shí)使用。例如,以下基于 JScript? 的代碼展示了一種使用 MSXML 4.0 實(shí)現(xiàn)這個(gè)功能的簡(jiǎn)單方法:
var sc = new ActiveXObject("MSXML2.XMLSchemaCache.4.0);sc.add("http://example.org/employee/", "employee.xsd");var dom = new ActiveXObject("MSXML2.DOMDocument.4.0");dom.schemas = sc;if (dom.load("employee.xml"))WScript.echo("success: document conforms to Schema");elseWScript.echo("error: invalid instance");
它在 Microsoft? .NET 和大多數(shù)其他可識(shí)別 XML 架構(gòu)的處理器中的運(yùn)行方式都是類似的。
您可以從本文頂部的鏈接下載一個(gè)命令行驗(yàn)證實(shí)用工具,以試驗(yàn)本專欄中討論的原理。該驗(yàn)證實(shí)用工具允許您指定想驗(yàn)證的實(shí)例文檔以及所需要的任意多的架構(gòu)定義。命令行用法如下:
c:>validate instance.xml -s schema1.xsd -s schema2.xsd ...
XML架構(gòu)還提供了 schemaLocation 屬性,用于在實(shí)例文檔中提供關(guān)于所需架構(gòu)定義位置的提示。schemaLocation 屬性位于http://www.w3.org/2001/XMLSchema-instance 命名空間中,該命名空間是專門為只在實(shí)例文檔中使用的屬性而保留的。從現(xiàn)在起,我會(huì)將這個(gè)命名空間綁定到 xsi 前綴。xsi:schemaLocation 屬性采用一個(gè)以空格為分隔符的命名空間標(biāo)識(shí)符和 URL 對(duì)的列表,如下所示:
<tns:employee xmlns:tns="http://example.org/employee/"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://example.org/employee/http://develop.com/aarons/employee.xsd"/>
在這種情況下,如果處理器還沒(méi)有訪問(wèn)到適合于http://example.org/employee/ 命名空間的架構(gòu)定義,則可以從http://develop.com/aarons/employee.xsd 下載。
元素和屬性
使用 xsd:element 和 xsd:attribute 元素,可以分別將元素和屬性定義為 targetNamespace 的一部分。例如,假定您要描述以下可識(shí)別命名空間的實(shí)例文檔:
<tns:employee xmlns:tns="http://example.org/employee/"tns:id="555-12-3434"><tns:name>Monica</tns:name><tns:hiredate>1997-12-02</tns:hiredate><tns:salary>42000.00</tns:salary></tns:employee>
完成該操作最簡(jiǎn)單的方法是通過(guò)以下架構(gòu)定義:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"targetNamespace="http://example.org/employee/"><xsd:element name="employee"/><xsd:element name="name"/><xsd:element name="hiredate"/><xsd:element name="salary"/><xsd:attribute name="id"/></xsd:schema>
請(qǐng)注意,僅在 xsd:schema 元素中放置 xsd:element/xsd:attribute 聲明,會(huì)自動(dòng)將其與 http://example.org/employee/ 命名空間相關(guān)聯(lián)。由于這些聲明是根 xsd:schema 元素的子級(jí),它們?cè)诩軜?gòu)中被認(rèn)為是全局的。
由于該架構(gòu)把這些元素/屬性指定為 http://example.org/employee/ 命名空間的組成部分,它們?cè)趯?shí)例文檔中必須與該命名空間相關(guān)聯(lián)(就像我在前面 列出的原始實(shí)例一樣)。對(duì)實(shí)例進(jìn)行細(xì)微的命名空間更改將導(dǎo)致它無(wú)效。例如,您應(yīng)考慮以下包含不合格的 name、hiredate 和 salary 元素以及不合格 id 屬性 的文檔:
<tns:employee xmlns:tns="http://example.org/employee/"id="555-12-3434"><name>Monica</name><hiredate>1997-12-02</hiredate><salary>42000.00</salary></tns:employee>
因?yàn)榍懊娴募軜?gòu)定義說(shuō)明這些元素/屬性均來(lái)自 http://example.org/employee/ 命名空間,而這次它們未與命名空間相關(guān)聯(lián),所以根據(jù)架構(gòu)這個(gè)實(shí)例無(wú)效。
即使是一個(gè)細(xì)微的更改也會(huì)修改原始文檔,所以它使用默認(rèn)的命名空間聲明,而不是一個(gè)命名空間前綴:
<employee xmlns="http://example.org/employee/"id="555-12-3434"><name>Monica</name><hiredate>1997-12-02</hiredate><salary>42000.00</salary></employee>
雖然在此情況下,所有的元素都與默認(rèn)命名空間 (http://example.org/employee/) 關(guān)聯(lián),但 id 屬性仍然是不合格的,因?yàn)槟J(rèn)命名空間不適用于屬性。因此,根據(jù)架構(gòu),這個(gè)文檔實(shí)例也被認(rèn)為是無(wú)效的。
如您所見(jiàn),XML 命名空間恰恰是 XML 架構(gòu)的核心。在使用 XML 架構(gòu)時(shí),必須全面了解命名空間是如何工作的,因?yàn)槿绻麑?shí)例文檔與架構(gòu)指定的不一致,則它會(huì)無(wú)效。
您可能已經(jīng)注意到這個(gè)簡(jiǎn)單示例既沒(méi)有限制任何元素的內(nèi)容,也未定義命名空間中各元素之間的結(jié)構(gòu)關(guān)系。它等效于以下 DTD(暫時(shí)省略了屬性聲明):
<!ELEMENT employee ANY><!ELEMENT name ANY><!ELEMENT hiredate ANY><!ELEMENT salary ANY>
因此,即使以下 XML 實(shí)例文檔沒(méi)有任何意義,根據(jù)架構(gòu),該文檔也是有效的:
<tns:name xmlns:tns="http://example.org/employee/"><tns:employee><tns:hiredate>42.000</hiredate><tns:salary tns:id="555-12-3434">Monica</tns:salary></tns:employee></tns:name>
XML 架構(gòu)使通過(guò)復(fù)雜類型定義描述元素的結(jié)構(gòu)成為可能。
定義復(fù)雜類型
使用 DTD,一個(gè)元素的內(nèi)容模型在 ELEMENT 聲明中定義,如下所示:
<!ELEMENT employee (name, hiredate, salary)>
該 ELEMENT 聲明表明一個(gè) employee 元素包含一個(gè) name 元素,然后是一個(gè) hiredate 元素,最后是一個(gè) salary 元素。
XML 架構(gòu)能夠用在 xsd:element 聲明中嵌套 xsd:complexType 元素的類似方式來(lái)定義元素內(nèi)容模型,如下所示:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"targetNamespace="http://example.org/employee/"><xsd:element name="employee"><xsd:complexType><!-- employee‘s content model goes here --></xsd:complexType></xsd:element></xsd:schema>
XML 架構(gòu)模型更像一門編程語(yǔ)言,在其中,您可以將變量綁定到正式類型定義。xsd:complexType 允許您定義一個(gè)傳遞其結(jié)構(gòu)的元素類型。在元素聲明中嵌套 xsd:complexType 會(huì)有效地將其綁定到那個(gè)元素(類似一個(gè)變量)。從類型定義方面進(jìn)行思考是從 DTD 工作方式的一個(gè)主要的形式轉(zhuǎn)變。
您放入 xsd:complexType 元素中的內(nèi)容與您放入DTD ELEMENT 聲明括號(hào)中的內(nèi)容是類似的。前面的 employee ELEMENT 聲明指定了 name、hiredate 和 salary 元素的順序。使用管道 (|) 分隔符而不是逗號(hào)改變了一個(gè)元素選擇的意義:
<!ELEMENT employee (name | hiredate | salary)>
在 XML 架構(gòu)中,您通過(guò)一個(gè)排序元素指定內(nèi)容模型的特征,它被作為 xsd:complexType 元素的子級(jí)進(jìn)行嵌套。XML 架構(gòu)定義了三個(gè)排序元素: xsd:sequence、xsd:choice 和 xsd:all (如
圖 3 所示)。
xsd:sequence 和 xsd:choice 元素等效于上述所示的 DTD 示例。然而,xsd:all 是一個(gè)新概念 — 它指定內(nèi)容模型由以任何順序排列的所有項(xiàng)組成。 DTD 語(yǔ)法中沒(méi)有設(shè)計(jì)這種概念,但是您可以像下面所示的那樣通過(guò)顯式地指定所有可能的排列來(lái)定義此類語(yǔ)義:
<!ELEMENT employee ( (name, hiredate, salary) |(name, salary, hiredate) |(hiredate, name, salary) |(hiredate, salary, name) |(salary, name, hiredate) |(salary, hiredate, name) ) >
如您所見(jiàn),組合數(shù)學(xué)很快開(kāi)始對(duì)您造成不利了。由于 "all" 像排列和組合一樣,是一個(gè)一級(jí)排序,所以XML 架構(gòu)方法更為簡(jiǎn)潔。
排序元素可以包含對(duì)全局元素聲明、局部元素聲明、其他排序以及一些其他諸如通配符和組引用等構(gòu)造的引用。
圖 4 所示的架構(gòu)示例說(shuō)明了如何定義一個(gè)對(duì)在架構(gòu)其他地方定義的全局元素進(jìn)行引用的 xsd:complexType。
請(qǐng)注意,ref 屬性采用了一個(gè)帶有前綴的元素名稱。切記,一旦在架構(gòu)中聲明了一個(gè)全局元素,它就會(huì)自動(dòng)地與 targetNamespace 相關(guān)聯(lián)。當(dāng)您按 name 引用全局元素時(shí),它們將被看作合格的 name。如果使用 ref="name" 而不是 ref="tns:name",架構(gòu)處理器則會(huì)尋找未與命名空間相關(guān)聯(lián)(或默認(rèn)命名空間,如果有一個(gè)已在使用)的 name 元素,但是不會(huì)找到,因?yàn)樵诩軜?gòu)中聲明的唯一 name 元素是來(lái)自 http://example.org/employee/ 命名空間的 name 元素。
如果我已經(jīng)把 http://example.org/employee/ 作為文檔的默認(rèn)命名空間,我就能夠在不使用命名空間前綴(例如,ref="name")的情況下引用全局元素名稱了,如
圖 5 所示。
圖 4和
圖 5 中的示例架構(gòu)在邏輯上是等效的 — 它們只是以稍微不同方式進(jìn)行了序列化。兩個(gè)示例架構(gòu)都對(duì) employee 元素的內(nèi)容進(jìn)行了限制。現(xiàn)在,employee 元素必須包含 name、hiredate 和 salary 元素,并且所有這些元素都必須與 http://example.org/employee/ 命名空間相關(guān)聯(lián)。
局部元素聲明
由于 employee 是我計(jì)劃在實(shí)例文檔中作為頂層元素使用的唯一元素,因此實(shí)在沒(méi)有理由將 name、hiredate 和 salary 定義為全局元素。相反,我僅僅能夠在 employee 元素的內(nèi)容模型中局部定義 name、hiredate 和 salary 元素。
例如,
圖 6中顯示的架構(gòu)包含了一個(gè) employee 元素聲明,聲明中包含了一個(gè)局部元素聲明的序列。在這個(gè)示例中,name、hiredate 和 salary 元素實(shí)際上作為 employee 元素的一部分進(jìn)行聲明,而且不能在實(shí)例的其他位置使用。只有 employee 元素聲明作為根 xsd:schema 元素的一個(gè)子級(jí)全局出現(xiàn)。這就提出了一個(gè)有趣的問(wèn)題:局部元素應(yīng)該與目標(biāo)命名空間相關(guān)聯(lián)嗎?
局部范圍和命名空間
為了有助于理解該問(wèn)題的答案,讓我們以支持命名空間的編程語(yǔ)言(如 C#)編寫的類似示例為例。請(qǐng)仔細(xì)查看以下在 "example" 命名空間中定義的C# 類定義:
namespace example {public class employee {public string name;public string hiredate;public double salary;}}
在該命名空間中,什么標(biāo)識(shí)符是真正可見(jiàn)的?只有一個(gè) — employee。name、hiredate 和 salary 標(biāo)識(shí)符只是在 employee 類中可見(jiàn)。因此,您需要用命名空間標(biāo)識(shí)符而不是局部成員名稱來(lái)限定 employee,如下所示:
// employee is namespace-qualifiedexample.employee c = new example.employee();// local members are unqualifiedc.name = "Monica";c.hiredate = "1997-12-02";c.salary = 42000.00;// this does not work, nor make sense// c.example.name = "Monica";
XML 架構(gòu)設(shè)計(jì)者好象在沿著局部范圍的思路進(jìn)行思考,因?yàn)樵谀J(rèn)模式下它是以這種同樣的方式工作的。在 XML 架構(gòu)中,實(shí)例文檔中只有全局元素需要用目標(biāo)命名空間限定,而且局部元素必須保持為非限定的。下面顯示的 XML 文檔是
圖 6所示的完整架構(gòu)的一個(gè)有效實(shí)例:
<!-- global element qualified --><tns:employee xmlns:tns="http://example.org/employee/"><!-- local elements unqualified --><name>Monica</name><hiredate>1997-12-02</hiredate><salary>42000.00</salary></tns:employee>
如果修改該實(shí)例以便 name、hiredate和 salary 元素均由 http://example.org/employee/ 命名空間限定,根據(jù)架構(gòu),它會(huì)變得無(wú)效。切記,即使是對(duì)默認(rèn)命名空間聲明進(jìn)行細(xì)微的改變也會(huì)導(dǎo)致該結(jié)果的發(fā)生。
由于不是每個(gè)人都滿意該方法,XML 架構(gòu)設(shè)計(jì)者使在實(shí)例中控制對(duì)局部元素應(yīng)該進(jìn)行限定還是取消限定成為可能。您可以在元素/元素基礎(chǔ)上通過(guò)窗體屬性對(duì)此進(jìn)行控制,如下所示:
<xsd:element name="employee"><xsd:complexType><xsd:sequence><!-- local element declarations --><xsd:element name="name" form="qualified"/><xsd:element name="hiredate"/><xsd:element name="salary" form="qualified"/></xsd:sequence></xsd:complexType></xsd:element>
employee元素的一個(gè)有效實(shí)例現(xiàn)在將具有一個(gè)限定的子級(jí) name 元素和一個(gè)非限定的子級(jí) hiredate 元素,在其后是一個(gè)限定的子級(jí) salary 元素,如該有效實(shí)例所示:
<tns:employee xmlns:tns="http://example.org/employee/"><tns:name>Monica</tns:name><hiredate>1997-12-02</hiredate><tns:salary>42000.00</tns:salary></tns:employee>
您還可以通過(guò) elementFormDefault 屬性切換架構(gòu)中所有局部元素聲明的默認(rèn)設(shè)置,如下所示:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"targetNamespace="http://example.org/employee/"elementFormDefault="qualified">•••</xsd:schema>
現(xiàn)在,在默認(rèn)情況下,所有局部元素都必須在實(shí)例中進(jìn)行限定(假定還沒(méi)有使用窗體屬性重寫特定元素的設(shè)置),如該有效實(shí)例所示:
<tns:employee xmlns:tns="http://example.org/employee/"><tns:name>Monica</tns:name><tns:hiredate>1997-12-02</tns:hiredate><tns:salary>42000.00</tns:salary></tns:employee>
由于所有元素在這種情況下都是合格的,用戶現(xiàn)在可以選用一個(gè)默認(rèn)的命名空間聲明和仍將有效的實(shí)例:
<employee xmlns="http://example.org/employee/"><name>Monica</name><hiredate>1997-12-02</hiredate><salary>42000.00</salary></employee>
出現(xiàn)約束
在 DTD 中,您可以通過(guò) *、+ 和 ? 修飾符控制內(nèi)容模型中元素出現(xiàn)的次數(shù)。XML 架構(gòu)擺脫了這些修飾符并僅定義了兩個(gè)屬性,minOccurs 和 maxOccurs,它們可以在元素聲明、排序和一些其他架構(gòu)構(gòu)造中使用。
一個(gè)項(xiàng)必須出現(xiàn)的最小次數(shù)和它能出現(xiàn)的最大次數(shù)分別由 minOccurs 和 maxOccurs 指定。兩個(gè)屬性的默認(rèn)值都是 1。您還可以使用 maxOccurs 中 的 "unbounded" 值來(lái)指定可以接受的無(wú)限次出現(xiàn)數(shù)。
考慮以下 DTD ELEMENT 聲明:
<!ELEMENT employee ( (fname, (middle | mi)?, lname, lname?),(project, role)* )>
可以使用一些嵌套的排序和 minOccurs/maxOccurs 在 XML 架構(gòu)中對(duì) ELEMENT 聲明進(jìn)行重寫,如
圖 7所示。
復(fù)雜類型和屬性
使用 DTD 時(shí),屬性是為一個(gè)特定的元素定義的。下面的 ATTLIST 聲明將 id 屬性和 employee 元素相關(guān)聯(lián):
<!ELEMENT employee (name, hiredate, salary)><!ATTLIST employee id CDATA #REQUIRED>
在 DTD 中定義全局屬性是不可能的。它們始終必須同一個(gè)特定元素相關(guān)聯(lián),如上所示。
XML 架構(gòu)使得屬性可以像元素那樣進(jìn)行全局和局部定義。全局屬性是通過(guò)在根 xsd:schema 元素中使用 xsd:attribute 元素定義的。所示的第一個(gè)架構(gòu)示例定義了一個(gè)命名為 id 的全局屬性。全局屬性是指您在各種情況下都不能預(yù)見(jiàn)它們會(huì)在何處使用。
屬性也能夠包含在一個(gè) xsd:complexType 定義中,使它們對(duì)于該特定類型而言是局部的。在 xsd:complexType 元素中使用時(shí),xsd:attribute 元素必須在子級(jí)排序之后,如下面全局元素聲明所示:
<xsd:element name="employee"><xsd:complexType><xsd:sequence><xsd:element name="name"/></xsd:sequence><xsd:attribute name="id"/></xsd:complexType></xsd:element>
對(duì)于元素,您必須限定全局屬性并且不能在實(shí)例文檔中通過(guò)默認(rèn)方式限定局部屬性。以下是以前定義的 employee 元素的一個(gè)有效實(shí)例:
<tns:employee xmlns:tns="http://example.org/employee/"id=‘555-12-3434‘><name>Monica</name></tns:employee>
然而,對(duì)于元素,如果您希望限定局部屬性,可以通過(guò)窗體或 attributeFormDefault 屬性改變?cè)撔袨椤?div style="height:15px;">
到此為止,我一直在通過(guò) xsd:complexType 定義來(lái)定義元素的類型(或結(jié)構(gòu))。然而,在和新聲明的元素聯(lián)系時(shí),這些類型定義不會(huì)被命名。該方法類似于在 C++ 中使用匿名類型。例如,以下 C++ 代碼將 pt 變量映射到它前面的匿名結(jié)構(gòu):
XML 架構(gòu)也支持已命名類型,實(shí)際上因其有價(jià)值的重用潛力,所以才成為大多數(shù)開(kāi)發(fā)人員首選的方法。您不但能夠在單個(gè)架構(gòu)中重用已命名類型,而且還能夠通過(guò) xsd:include 和 xsd:import 元素在各架構(gòu)間重用已命名類型。
您通過(guò)名稱屬性來(lái)命名 xsd:complexType。然后,能夠通過(guò)類型屬性將元素聲明綁定到一個(gè)已命名類型。以下架構(gòu)闡釋了該元素綁定如何運(yùn)行:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:tns="http://example.org/employee/"targetNamespace="http://example.org/employee/"><xsd:complexType name="EmployeeType"><xsd:sequence><xsd:element name="name"/><xsd:element name="hiredate"/><xsd:element name="salary"/></xsd:sequence></xsd:complexType><xsd:element name="employee" type="tns:EmployeeType"/></xsd:schema>
請(qǐng)注意,由于 xsd:complexType 定義現(xiàn)在是架構(gòu)中的一個(gè)全局構(gòu)造,它會(huì)被自動(dòng)地與 targetNamespace 相關(guān)聯(lián)。這意味著您在引用類型屬性中的 EmployeeType 時(shí),必須使用限定名稱。您可回溯本文中顯示的所有示例架構(gòu),并將匿名 xsd:complexType 轉(zhuǎn)換至已命名類型。在這兩種方法之間轉(zhuǎn)換很容易。
使用已命名類型的另一個(gè)好處在于,如果您不需要,則不必在架構(gòu)中實(shí)際使用全局元素聲明。相反,您可以通過(guò) xsi:type 屬性顯式地指定實(shí)例文檔中的元素類型,該屬性也來(lái)自 http://www.w3.org/2001/XMLSchema-instance 命名空間。例如,您應(yīng)考慮以下實(shí)例來(lái)完成該任務(wù):
<foo xsi:type="tns:EmployeeType"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:tns="http://example.org/employee/"><name>Monica</name><hiredate>1997-12-02</hiredate><salary>42000.00</salary></foo>
foo 元素在架構(gòu)的任何位置都未被聲明,但我已經(jīng)顯式地指定了它的類型,對(duì)于處理器這足以明了如何處理 foo 的內(nèi)容。該方法類似于大多數(shù)編程語(yǔ)言中的強(qiáng)制轉(zhuǎn)換。
我已經(jīng)介紹了通過(guò) xsd:element、xsd:attribute和 xsd:complexType 定義描述文檔結(jié)構(gòu)細(xì)節(jié)的主要內(nèi)容。然而,現(xiàn)在為止,您所看到的架構(gòu)還沒(méi)有描述 name、hiredate 和 salary 元素以及 id 屬性的特征。在這一點(diǎn)上,它們能夠合法地包含任何內(nèi)容。name、hiredate 和 salary 元素以及 id 屬性應(yīng)真正地包含文本并且每個(gè)使用特定的格式。
這就是XML 架構(gòu)比 DTD 大有進(jìn)步的地方,特別是對(duì)于軟件開(kāi)發(fā)人員。XML 架構(gòu)定義了一組內(nèi)置數(shù)據(jù)類型,可以用于約束純文本元素和屬性的內(nèi)容(參見(jiàn) 圖 8 )。每個(gè)數(shù)據(jù)類型具有一個(gè)顯式定義的值空間和一個(gè)顯式定義的詞法空間(或者也就是說(shuō),可能在 XML 文檔中使用的字符串格式)。例如,double 值 4200 能夠以各種詞法方式進(jìn)行表示(參見(jiàn) 圖 9)。關(guān)于給定的數(shù)據(jù)類型的值和詞法空間更詳細(xì)的內(nèi)容,請(qǐng)參閱 XML 架構(gòu)規(guī)范第二部分(相關(guān) URL 可在“
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:tns="http://example.org/employee/"targetNamespace="http://example.org/employee/"><xsd:complexType name="EmployeeType"><xsd:sequence><xsd:element name="name" type="xsd:string"/><xsd:element name="hiredate" type="xsd:date"/><xsd:element name="salary" type="xsd:double"/></xsd:sequence><xsd:attribute name="id" type="xsd:string"/></xsd:complexType><xsd:element name="employee" type="tns:EmployeeType"/></xsd:schema>
當(dāng)一個(gè)架構(gòu)處理器驗(yàn)證前一個(gè)架構(gòu)的示例時(shí),它會(huì)確保每個(gè)元素/屬性中包含的文本符合其定義類型的合法的詞法表示。
如您在 圖 8 中所見(jiàn),對(duì)于每種情況都有一個(gè)數(shù)據(jù)類型。然而,您一定會(huì)遇到內(nèi)置數(shù)據(jù)類型不能精確滿足您的需要的情況。例如,在以前的架構(gòu)定義中,id 屬性被定義為類型字符串,但它實(shí)際需要采用社會(huì)保障號(hào)格式。XML 架構(gòu)確實(shí)能夠?qū)崿F(xiàn)在這些情況下定義自定義簡(jiǎn)單類型,但我會(huì)將這個(gè)主題留給本專欄的第二部分。
我們所處的位置 XML 架構(gòu)克服了 DTD 中所有的局限和缺點(diǎn)。XML 架構(gòu)語(yǔ)法是 XML 1.0。XML 架構(gòu)完全圍繞命名空間進(jìn)行設(shè)計(jì)。而且,更重要的是,XML 架構(gòu)支持典型的編程語(yǔ)言數(shù)據(jù)類型以及自定義的簡(jiǎn)單和復(fù)雜類型。
盡管 W3C 最近才發(fā)布了最終的 XML 架構(gòu)建議(2001 年 5 月),但在各種 XML 和 Web 服務(wù)相關(guān)的基礎(chǔ)結(jié)構(gòu)中已經(jīng)存在了對(duì)于該規(guī)范的廣泛支持。該基礎(chǔ)結(jié)構(gòu)依靠 XML 架構(gòu)來(lái)自動(dòng)生成 XML 處理代碼、構(gòu)建即時(shí)動(dòng)態(tài)代理/存根、在編輯器和其他工具中提供 IntelliSense? 以及通過(guò)架構(gòu)驗(yàn)證技術(shù)來(lái)簡(jiǎn)化錯(cuò)誤處理。MSXML 4.0、SOAP Toolkit 2.0 和 .NET 都是使用 XML 架構(gòu)可以完成的絕好示例。
本月的專欄只涉及了 XML 架構(gòu)的基礎(chǔ)知識(shí)。我介紹了 DTD 提供的所有功能和另外一些內(nèi)容。在該系列的第二部分,我將向您展示該語(yǔ)言的更高級(jí)功能。
Aaron Skonnard 是 DevelopMentor 的教師兼研究員。在那里,他開(kāi)發(fā)了關(guān)于 XML 和 Web 服務(wù)的課程。Aaron 與人合著了《Essential XML Quick Reference》(Addison-Wesley, 2001) 和《Essential XML》(Addison-Wesley, 2000)。您可以通過(guò)以下網(wǎng)址與 Aaron 聯(lián)系: