許多開發者都期待著 XML 模式能夠很快取代 DTD 用於指定 XML 文檔類型。盡管 David Mertz 相信 XML 模式在開發者寶庫中是一種無價工具,但他對該模式將替代 DTD 持懷疑態度。“XML 問題”專欄的這一部分逐步嘗試對模式和 DTD 進行比較,並闡明在 XML 模式世界中發生的事件。
雖然 W3C XML Schema在許多場合中勝過 DTD,但仍然還有一些 DTD更勝一籌的領域。開發者要不斷地進行艱難的選擇(這在 XML世界中是司空見慣的事)。讓我們開始對其中一些選項進行區分。
目前形勢
將 XML用作數據表示格式的主要原因是有可能指定文檔的結構化需求:確定究竟什麼類型的內容和子元素可以出現在元素中的一些規則(以及以什麼順序和基數性等)。在傳統SGML 派系中,文檔規則的表示曾經是 DTD -- W3C XML 1.0建議書的正式規范確實明確提供了 DTD。不過,有一些 DTD無法實現的相當常見的約束;DTD的主要限制在於它們缺乏數據類型的表達(可以指定一個元素必須包含PCDATA,但無法指定它必須包含例如 nonNegativeInteger )。 還有一個次要問題,即 DTD無法簡化子元素基數性的規范(可以簡潔地指定“一個或多個”子元素,但即使可能,指定“七到十二之間”也會過分冗長,甚至完全曲解)。
為了對付 DTD的各種限制,一些 XML用戶曾呼吁一些指定文檔規則的替代方法。總是有可能從編程上檢查 XML文檔中的條件,但從本質上說,往往更需要施加更嚴格的標准,即“一個不滿足一組正式規則的文檔就是 無效文檔”。W3C XML Schema是滿足這些要求的一個主要答案(但不是這裡唯一可供選擇的模式)。StevenHolzner 在 XML 內幕中將 XML模式歸納為具有以下特征,值得在這裡重申:
隨著時間的推移,許多人都向 W3C 抱怨 DTD太復雜,並要求使用一些更簡單的方法。W3C聽取並指定了一個委員會來解決這一問題,然後拿出了一個比以往任何 DTD都復雜得多的解決方案 (p.199)。 Holzner 繼續道 -- 幾乎所有XML 程序員都同意(包括我自己)-- 如果不考慮其復雜性,W3C XML Schema還是提供了許多重要能力,並值得用於許多確認規則類。
至少有兩個基本的和概念性的缺陷存在於所有“到處模式”的目標中。第一個問題是剛剛於2000 年 12 月 15 日結束其復審階段的 W3C XML Schema CandidateRecommendation,不包含任何實體;通過擴展,它可以包括參數實體。第二個問題是,盡管存在增強的表達方式,仍然有許多文檔規則不能用XML 模式表示(一些建議提議利用 XSLT來增強確認表達,但也可能存在和使用其它方法)。換句話說,模式無法執行DTD長期以來能夠執行的所有操作,而從另一方面來講,模式也無法表達人們希望對文檔施加的進一步規則的完整集合。從更實際的角度來說,用於XML 模式的工具不如用於 DTD的工具來得成熟(特別在確認方面,這是核心問題)。
XML文檔確認規則整體上仍處於混亂狀態。不幸的是,我無法預言每件事的最終結果將會怎樣。(有關何時使用 DTD 可能比較有意義的摘要,請參閱側欄“ 何時使用DTD。”)同時,讓我們看看 DTD 和 XML模式能夠表達哪些內容的一些具體細節。
豐富的類型
W3C XML Schema真正出色的地方是在表達屬性值和元素內容的類型約束上。而這恰恰是 DTD最薄弱的地方。除了提供非常豐富的一組內置 simpleType 以外,XML 模式還允許您使用類似規則表達式的語法派生出新的 simpleType 。內置類型包括您在使用編程語言時遇到的: string 、 int 、 float 、 unsignedLong 、 byte 等等;但它們還包括大多數編程語言生來不具備的一些類型: timeInstant (即日期/時間)、 recurringDate (年中的天)、 uriReference 、 language 、 nonNegativeInteger 。
例如,使用 DTD時,將有類似於清單 1 中的聲明:
清單 1:DTD "item" 元素定義
<!ELEMENT item (prodName+,USPrice,shipDate?)
<!ATTLIST item partNum CDATA>
<!ELEMENT prodName (#PCDATA)>
<!ELEMENT USPrice (#PCDATA)>
<!ELEMENT shipDate (#PCDATA)>
使用 W3C XML Schema時,聲明可以更具體(對 W3C Schema 的最初規定稍有修改):
清單 2:XML 模式 "item"元素定義
<xsd:element name="item">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="prodName" type="xsd:string" maxOccurs="5"/>
<xsd:element name="USPrice" type="xsd:decimal"/>
<xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="partNum" type="SKU"/>
</xsd:complexType>
</xsd:element>
<!-- Stock Keeping Unit, a code for identifying products -->
<xsd:simpleType name="SKU">
<xsd:restriction base="xsd:string">
<xsd:pattern value="d{3}-[A-Z]{2}"/>
</xsd:restriction>
</xsd:simpleType>
從表面上看,這些元素定義中有兩個顯著特性。首先是模式本身是格式完整的 XML 實例,其標記使用 xsd 名稱空間(實際上,DTD也是這樣,但它只有處理指令,而沒有這樣的內容);其次(根據第一點的結論)是模式遠比DTD 繁瑣。
除了語義方面的准確性以外,還可以看到模式示例能夠執行一些DTD 不可能完成的操作。 prodName 類型在定義之間基本上是相同的,但模式中的 USPrice 和 shipDate 規范分別是 decimal 和 date 類型。 作為文本文件,具有這些元素的 XML實例在元素內部包含一些 ASCII(或Unicode)字符;不過,具有模式意識的確認器可以在 decimal 和 date 元素內部要求更具體的字符格式(其它類型也是一樣)。 更有趣的是屬性 partNum ,它屬於派生的專門類型。類型 SKU 不是內置類型,而是跟在 "SKU"聲明中給定模式後的一系列字符(具體來說,它必須有三位:一個連線和兩個大寫字母,按這樣的順序)。也有可能將 SKU 用於元素類型;它在這種情況下定義屬性只是一種巧合。
在元素定義的 DTD版本中,所有這些有趣的(如果是專門的,也可能相當復雜)類型一定簡單地稱之為 PCDATA ,至於字符數據是什麼樣沒有更多說明(在屬性情況中是 CDATA )。
在類型豐富的元素/屬性值中,模式巧妙地從描述 XML實例的語法漸變到描述其語義。語法分析純化論者可能會就我的描述提出異議:“內置模式類型是從語法上定義的,因此構建在這些內置類型上的模式在形式上也是符合語法的。”但在實際情況中,當聲明一個給定的元素必須是 date 時,您實際上希望的是讓元素包含一個日期。當然,表達語義信息不是件壞事,但有人會爭論說最好同樣將它限制在應用程序級別,而不是格式聲明。畢竟有一些語義特性 -- 即使是簡單的特性 --避開了模式,但在應用程序中和模式所表達的內容一樣重要。 例如,當然"stock-keeping unit" 必須類似於"999-AA";但可能您還提供在十三以內的小裝置。 integer 被13 整除性不能使用 XML 模式表達(因此您仍然無法在這一級別上為 widgetquantity提供所需的約束)。這裡的重點是說,即使有模式(勝過DTD)的額外能力,仍可能需要在應用程序級別上執行後確認來確定 XML文檔是否 在功能上有效。
何時使用DTD
遇到以下情況時,DTD 仍然是您的首選:
文檔規則的簡潔表示很重要。
希望下游用戶能夠通過內部參數集覆蓋並將類型專門化。
您的文檔規則主要考慮元素的嵌套而不是內容的語義約束(如同使用散文標記)。
慣常使用的工具支持 DTD 勝於支持模式。
出現約束
除了強大的類型聲明以外,XML 模式還在 DTD聲明子元素模式的基數性能力的基礎上進行了改進。不過,DTD用於表達每個出現約束(基數性)的方法比 XML模式總是笨拙許多。
在 DTD中,符號: ? 、 * 和 + 分別指定“零或一”、“零或多個”、“一個或多個”,其中一個量化了基數性。即,除了問號有能力說:“有或沒有”以外,DTD語法中似乎沒有可以限制給定模式出現次數的東西(無論是單一子標記還是嵌套的一系列)。因此如何表達上面模式示例中 1-5 個 prodName 的出現似乎是個問題。同樣地,如果沒有 XML 模式屬性 minOccurs ,我們似乎就無法表達讓某個事物出現特定次數的需求(而非“至少一次”)。實際上,DTD的最小量詞雖然有時不是很講究,但已經夠好的了。下面是等價的約束:
清單 3:“七到十二”個甜面圈的 XML模式語法
<xsd:element name="donutorder">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="donut" type="xsd:string"
minOccurs="7" maxOccurs="12" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<!ELEMENT donut (#PCDATA)>
<!ELEMENT donutorder
(donut,donut,donut,donut,donut,donut,donut,
donut?,donut?,donut?,donut?,donut?)
當然,如果獲得大筆訂單,DTD看上去開始 真的很糟糕!
枚舉
無論是 DTD 還是 W3C XML Schema都允許在屬性中使用枚舉類型,但模式是非常了不起的進步,因為它們還可以在元素內容中允許使用枚舉類型。我認為,DTD真正的缺點在於它缺乏這種能力。而且,模式的枚舉方法非常全面而優秀。一個專門的 simpleType 可以包含一個枚舉 面。這樣的 simpleType 自動適合於屬性或元素值類型。
讓我們舉例說明每種語法:
清單 4:用於枚舉屬性的 XML模式語法
<xsd:simpleType name="shoe_color">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="red"/>
<xsd:enumeration value="green"/>
<xsd:enumeration value="blue"/>
<xsd:enumeration value="yellow"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="person" type="person_type">
<xsd:attribute name="shoes" type="shoe_color"/>
</xsd:element>
<!ATTLIST person shoes (red | green | blue | yellow)>
DTD屬性聲明看來還算不錯(可能在簡明性方面更好),但如果您的模型在元素內容中放入 shoe_color ,DTD 就變得很直截了當:
清單 5:用於枚舉元素的 XML模式語法
<xsd:element name="shoes" type="shoe_color">
目的
W3C XML Schema 可以讓 XML程序員表達一組新的對文檔的聲明性約束,而這是 DTD做得不夠的地方。對許多程序員來說,在模式中使用 XML 實例語法還會對XML任務的不同部分產生更大的不一致性(當然,其他人不同意這種說法)。模式的目標當然是隨著它們為人們逐步了解,以及開發者增強更多用於它們的工具,而在重要性和作用域方面得到增長。
一種在模式工作開始時得到飛躍的方法是將從現有 DTD 到 XML模式格式的轉換自動化。很明顯,自動轉換無法對 XML模式本身添加新的表達能力;但自動化可以創建好的模板,從中指定人們希望施加的特定類型約束。 參考資料部分提供了到自動 DTD至模式轉換工具的兩個鏈接。