隨著 XML 越來越多地作為信息交換標准,持久、驗證和查詢 XML 文檔的功能也越來越重要。此外,隨著 Web 服務和 mash-up 應用程序的泛濫,Web 應用開發人員也更多地需要轉換 XML 消息,不論這些消息直接來自 Web 服務還是間接來自持久這些消息的數據庫。
多數商業數據庫管理系統已經以某種形式支持 XML 持久性。比如,IBM DB2® pureXML™天生就支持在 XML 類型列中存儲 XML 文檔、用 XML 模式驗證 XML 文檔、使用 XQuery 和 SQL/XML 查詢語言查詢 XML 文檔。可以在同一列中存儲和查詢模式不同但結構良好的 XML 文檔。
此外,不斷變化的 Web 環境要求數據庫適應新的功能(比如組織業務的擴展)和新數據類型(比如 RSS 提要消息)。可能每六個月就出現新的模式版本 — 有時候甚至只有兩周。信息技術架構師、應用程序開發人員和數據庫管理員經常發現,如果應用程序操作的 XML 文檔列使用不同版本的模式,管理起來就非常亂。另外,數據庫之上的應用程序及其用戶仍然需要和其交互,不論模式如何變化。
由於上述這些原因,模式演化作為一項重要課題需要在關系、面向對象和 XML 數據庫環境中加以研究。具體來說,對於 XML 數據庫,模式演化是 XML 的迫切要求。雖然以前的大部分工作考慮的是模式演化的技術方面(即如何有效存儲不同的模式,在變化中保持數據一致性),本文的目標是討論如何在不斷演化的模式中保持查詢的完整性。只要按照這裡提供的建議操作,數據庫就能跟上 Web 應用程序發展的需要,Web 應用程序仍然能夠保持與數據庫的交互(通過查詢)。這方面研究具有重要的意義:
我們考慮了在 XML 模式演化過程中最常見的變化類型並提出了一種分類方法。
按照這種分類,說明每種變化對模式驗證的影響。同時考慮到了向前兼容(即舊模式的文檔和新模式兼容)和向後兼容(即新模式的文檔和舊模式兼容)。
我們提出並討論了模式演化對查詢公式的影響。換句話說,對舊模式文檔執行的查詢不一定能用於新模式文檔(反之亦然)。
我們提出了管理 XML 模式演化的基本原則,說明如何控制模式變化以及如何編寫跨越不同模式版本的查詢。
下面介紹 XML 和 XQuery 的一些基本概念。“XML 模式變化分類” 一節提出了模式演化過程中可能出現的變化類型。“XML 模式演化對查詢的影響” 討論了這種影響,並通過保險業的例子說明了模式變化、查詢和查詢結果之間的交互。為了讓查詢在模式演化的時候仍然返回預期的結果,“管理 XML 模式演化的基本原則” 就此提出了管理模式演化的基本原則。
背景
XML(可擴展標記語言)是源於 SGML 的一種標記語言,其規范由 W3C 管理。使用 XML 有利於開發適用於多種環境的應用程序。XML 一直在 Web 服務應用程序中使用,RSS 提要是一個典型的例子。Web 服務也從消息傳遞環境中的結構化數據獲益。另外,XML 有助於建立文件格式的公開標准,促進不同平台、不同應用程序間的數據交換。此外,通用 XML 數據結構可以改進電子商務參與方之間的集成,更有效地交換訂單、庫存信息和發貨細節。XML 數據封裝也提高了訪問的機密性和可靠性,無論通過 Web 還是桌面應用程序訪問。
最後,可以為結構化 XML 數據實現高效的文檔搜索方法。和二進制數據不同,XML 是高度結構化的,很多解析器和工具針對 XML 數據搜索和查詢進行了優化。清單 1 是關於圖書館的一個簡單 XML 文檔。
清單 1. 簡單的 XML 文檔
<?XML version="1.0" encoding="UTF-8"?>
<library>
<book edition="5">
<title>Fundamentals of Database Systems</title>
<author>Ramez Elmasri</author>
<author>Shamkant B. Navathe</author>
<year>2006</year>
<publisher>Benjamin/Cummings</publisher>
<category>Computer ScIEnce
<subcategory>Databases</subcategory>
</category>
</book>
<book edition="3" format="hardcover">
<title>Mists of Avalon</title>
<info>Marion Zimmer Bradley, published by Del Rey</info>
<category>Fiction</category>
</book>
</library>
從這個例子可以看出,與關系數據不同,XML 數據通常是無模式和自描述的,但是展現出一種固有結構。這種結構主要通過元素(<library> 和 <book>)和屬性(edition 和 format)來定義。每個元素都有很多屬性(以及相應的值),元素中嵌套元素和文本內容。
通常,只有結構良好 和有效 的 XML 文檔才能被應用程序處理。符合 XML 語法規則的文檔是結構良好的。比如,XML 文檔必須有且只有一個根元素。符合 XML 模式的文檔是有效的。模式定義了文檔的組織規則和內容。比如,圖書館中的每個 <book> 必須有一個 <title> 元素,而且不能嵌套在其他 <book> 中。定義 XML 模式的多種語言之中,最常用的一種被簡稱為 XML Schema。
如果信息用 XML 文檔給出,可以用 XML 查詢語言如 XQuery 檢索數據。XQuery 使用 XPath 表達式訪問文檔的特定部分。清單 2 中給出了一個簡單的 XPath 表達式,檢索 2006 年出版的圖書。
清單 2. 簡單的 XPath 表達式
/library/book[year="2006"]//subcategory
XPath 表達式並入到通過 FLWOR(For、Let、Where、Order by、Return)表達式指定的 XQuery 語句中。比如,清單 3 中的 XQuery 表達式按標題順序返回 2006 年 Benjamin/Cummings 出版的圖書。
清單 3. XQuery
for $b in doc("mylibrary.XML")//book
where $b/publisher="Benjamin/Cummings"
order by $b/year
return $b/title
XML 模式變化的分類
在模式演化過程中,模式的任何成分都可能隨著版本的不同而變化。這一節介紹和驗證有關的不同的模式變動類型。首先可以分為基本變化和復雜變化。然後說明它們對驗證文檔的影響。
基本變化
FpML Architecture Working Group 圍繞 FpML 提出把模式的變化分為五種類型,FpML 是定義信息共享、處理、交換、派生及其他結構化產品的商業產品標記語言。表 1 列出了模式定義最常見的變化類型。同時說明對於每種變化,舊模式的文檔實例根據新模式是否有效(向前兼容),新模式的文檔實例根據舊模式是否有效(向後兼容)。
這些變化反映了不同行業的實際情況。但必須認識到,由於 XML 模式語言(比如 DTD 和 XML Schema)是擴展的,也可能出現其他類型的變化。此外,雖然一些復雜變化可歸為基本變化的組合(比如刪除後的精化),但為了 下一節 中更好的理解仍然單獨列出。請注意,大部分復雜變化,舊模式的文檔實例對於新模式通常都不再有效,或者需要應用程序的某些支持。通過 下一節 中的例子可以很清楚地看到這點。此外,基本變化中,只有精化和刪除可能引起兼容性問題,通過示例分析將很清楚看到這點。
表 1. 基本的模式變化及其對驗證的影響
復雜變化
表 1 中的變化很常見,但是沒有涵蓋所有可能的情況。本文擴展了這種分類,增加了幾種更復雜的變化類型,如 表 2 所示。
表 2. 復雜的模式變化及其對驗證的影響
XML 模式演化對查詢的影響
模式變化除了影響驗證之外,各種變化還影響查詢公式:針對舊模式文檔的查詢不一定適用於新模式文檔(反之亦然)。這一節通過一系列基於 ACORD(Association for Cooperative Operations Research and Development)模式的例子,討論模式演化對查詢及其結果的影響。ACORD 協會為保險、再保險及相關金融服務業制定標准。實際上,ACORD 平均每月發布一次新模式,示例中使用的變化僅僅是為了說明問題,不代表實際模式的變化。查詢的是金融交易中參與者的個人信息而不是 ACORD 標准的金融信息。
一般查詢
我們首先給出兩個一般的查詢,不訪問修改的模式元素。每個查詢都有自己的規范(類似於 XQuery 表達式)和基於 清單 4 所示實例文檔的結果(包括舊模式和新模式的文檔實例)。為了清晰,清單中包括實例的圖形化表示而不是模式規范。
清單 5、6 和 7 表明如果查詢不訪問修改過的元素,結果差不多。其中,清單 5 檢索每個實例的全部交易,對查詢沒有限制;清單 6 檢索交易的特定元素(姓氏),清單 7 和清單 6 相同,但是按交易 ID 對結果分組。這些例子很簡單,說明如果查詢中不涉及模式的演化部分就沒有什麼問題。但是,情況並非總是如此,如下一組例子所示。
清單 4. 模式修改的文檔實例:精化 <SSN> 元素,刪除 <BirthDate> 元素
舊模式的文檔實例
<TXLife>
<TXLifeRequest id="TXLifeRequest1001">
<TransRefGUID>2006-0712-1001</TransRefGUID>
<TransType tc="103">Schema Evolution </TransType>
<OLifE>
<Party id="ID1101">
<Person>
<FirstName>Alan</FirstName>
<LastName>Bird</LastName>
<Gender>MALE</Gender>
</Person>
</Party>
<Party id="ID1102">
<Person>
<FirstName>Anthony</FirstName>
<LastName>Bell</LastName>
<Gender>MALE</Gender>
<BirthDate>1975-11-14</BirthDate>
</Person>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
新模式的文檔實例
<TXLife>
<TXLifeRequest id="TXLifeRequest1002">
<TransRefGUID>2006-0712-1001</TransRefGUID>
<TransType tc="103">Schema Evolution </TransType>
<OLifE>
<Party id="ID1103">
<Person>
<FirstName>Carl</FirstName>
<LastName>Devon</LastName>
<Gender>MALE</Gender>
<SSN>606-23-0987</SSN>
</Person>
</Party>
<Party id="ID1104">
<Person>
<FirstName>Cinthia</FirstName>
<LastName>Din</LastName>
<Gender>FEMALE</Gender>
</Person>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
清單 5. 檢索 Schema Evolution 示例中全部交易
XQuery
for $trans in //TXLifeRequest return $trans
結果
結果是包含舊文檔和新文檔中都有的交易的 return 元素。
清單 6. 檢索所有 <LastName> 元素的值
XQuery
for $trans in //TXLifeRequest//LastName
return <result>{$trans}</result>
結果
<result>
<LastName>Bird</LastName><LastName>Bel</LastName>
<LastName>Devon</LastName><LastName>Din</LastName>
</result>
清單 7. 檢索所有 <LastName> 元素的值,按交易分組
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id let $t:=$trans//LastName
return
<TX>
{$tid}
<Names>
{$t}
</Names>
</TX>
結果
<result>
<LastName>Bird</LastName><LastName>Bel</LastName>
<LastName>Devon</LastName><LastName>Din</LastName>
</result>
基本變化
在前述五種基本變化類型中,我們主要討論精化 和刪除,因為它們影響查詢公式和結果。其他變化對查詢計算沒有顯著的影響(擴展 不會產生問題,除非新元素使用新的結構定義 — 這樣就相當於進行了精化;重釋 和重定義 對元素的結構沒有影響)。因此只有精化和刪除和下一節有關。這一節中討論的所有示例都是後面所述 基本原則 的基礎。
精化
清單 4 中的第一種基本變化是 ACORD 模式的擴展,增加了一個可選元素表示參與者的社會安全號碼(SSN)。清單 8 返回參與交易的所有人的姓氏,如果有 SSN 的話同時返回。因此,檢索了四個參與者(包括舊文檔和新文檔)和一個 SSN。雖然查詢訪問新元素,它的訪問也僅限於 return 子句 — 就是說是非限定性的。換句話說,清單 10 的 where 語句中有一個 exist 子句,要求具有 SSN 的那些人的名字。因此,查詢處理僅限於新模式文檔實例,因為舊模式的所有實例計算 where 語句的結果都是 false。清單 10 還說明如何編寫查詢才能保持跨模式的能力。
清單 8. 檢索姓氏和 SSN,按參與者和交易分組
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id
return
<TX>
{ $tid }
{ for $P in $trans//Party
return
<Party>
{$P//LastName}
{$P//SSN}
</Party> }
</TX>
結果
<TX id="TXLifeRequest1001">
<Party><LastName>Bird</LastName></Party>
<Party><LastName>Bell</LastName></Party>
</TX>
<TX id="TXLifeRequest1002">
<Party>
<LastName>Devon</LastName>
<SSN>606-23-0987</SSN>
</Party>
<Party><LastName>Din</LastName>
</Party>
</TX>
清單 9. 模式變化的文檔實例:元素組合
舊模式的文檔實例
<TXLife>
<TXLifeRequest id="TXLifeRequest1001">
<TransRefGUID>2006-0712-1001</TransRefGUID>
<TransType tc="103">Schema Evolution</TransType>
<OLifE>
<Party id="ID1101">
<Person>
<FirstName>Alan</FirstName>
<LastName>Bird</LastName>
<Gender>MALE</Gender>
</Person>
<Address>
<Line1>998 Mamaroneck Ave</Line1>
<City>White Plains</City>
<AddressStateTC tc="60">NY</AddressStateTC>
<Zip>10605</Zip>
</Address>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
新模式的文檔實例
<TXLife>
<TXLifeRequest id="TXLifeRequest1002">
<TransRefGUID>2006-0712-1001</TransRefGUID>
<TransType tc="103">Schema Evolution</TransType>
<OLifE>
<Party id="ID1103">
<Person>
<FirstName>Carl</FirstName>
<LastName>Devon</LastName>
<Gender>MALE</Gender>
</Person>
<Address>20 Fifth Ave, New York, NY 10011</Address>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
刪除
清單 4 中的第二項變化是從 ACORD 模式中刪除了 <BirthDate> 元素。清單 11 對第一個文檔實例返回出生日期列表,對第二個實例卻返回空白列表。如果只能檢索包含出生日期元素的交易,必須在 where 語句中使用 exists 函數,如 清單 10 所示。這裡僅計算舊模式文檔。
清單 10. 檢索提供 SSN 的人員的姓氏,按角色和交易分組
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id
where fn:exists($trans//SSN)
return <TX>{$tid}
{ for $P in $trans//Party
where fn:exists($P//SSN)
return
<Party> {$P//LastName}</Party> }
</TX>
結果
<TX id="TXLifeRequest1002">
<Party>
<LastName>Din</LastName>
</Party>
</TX>
清單 11. 檢索交易 ID 及出生日期列表
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id
return <TX>{$tid}
{ for $P in $trans//Party//BirthDate
return $P}
</TX>
結果
<TX id="TXLifeRequest1001">
<BirthDate>1975-11-14</BirthDate>
</TX>
<TX id="TXLifeRequest1002"/>
清單 12. 檢索 TXLifeRequest ID 和地址列表
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id return <TX>{$tid}
{ for $P in $trans//Address return $P}</TX>
結果
<TX id="TXLifeRequest1001">
<Address>
<Line1>998 Mamaroneck Ave</Line1>
<City>White Plains</City>
<AddressStateTC tc="60">NY</AddressStateTC>
<Zip>10605</Zip>
</Address>
</TX>
<TX id="TXLifeRequest1002">
<Address>20 Fifth Ave, New York,
NY 10011</Address>
</TX>
復雜變化
這一節討論復雜變化對查詢公式的影響。和基本變化一樣,每個查詢都有相當於 XQuery 語句的規范和結果。
元素組合
清單 9 描述了這種變化,<Address> 子元素由一個簡單元素(沒有子結構)組成。清單 12 給出了一個檢索地址的有趣的示例,因為它能從兩個實例中返回地址信息,但結果用不同的結構表示。
這類變化有兩方面的問題:
其一,新的組成元素可能和原來的結構元素不同名。這類變化的結果和重命名類似。
其二,查詢可能引用舊模式特定的子結構。比如,清單 14 請求地址中的郵政編碼。因此,結果僅返回第一個實例中的地址,因為第二個實例中不存在 <Zip> 元素。後一種情況對查詢的影響極其重要,因為即使沒有數據損失,數據的限定也沒有了。
清單 13. 模式變化的文檔實例:重命名
舊模式的文檔實例
<TXLife>
<TXLifeRequest id="TXLifeRequest1001">
<TransRefGUID>2006-0712-1001</TransRefGUID>
<TransType tc="103">Schema Evolution</TransType>
<OLifE>
<Party id="ID1101">
<Person>
<FirstName>Alan</FirstName>
<LastName>Bird</LastName>
<Gender>MALE</Gender>
</Person>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
新模式的文檔實例
<TXLife>
<TXLifeRequest id="TXLifeRequest1002">
<TransRefGUID>2006-0712-1001</TransRefGUID>
<TransType tc="103">Schema Evolution</TransType>
<OLifE>
<Party id="ID1103">
<Person>
<fName>Carl</fName>
<lName>Devon</lName>
<Gender>MALE</Gender>
</Person>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
清單 14. 檢索 TXLifeRequest ID 和地址的郵政編碼
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id
return <TX>{$tid}
{ for $P in $trans//Address//Zip return $P } </TX>
結果
<TX id="TXLifeRequest1001">
<Zip>10605</Zip>
</TX>
<TX id="TXLifeRequest1002"/>
元素分解
這裡的例子和 清單 9 的情況正相反:我們把新舊文檔顛倒一下。如果分解後的結構名稱保持不變,那麼查詢的惟一區別在於新模式的查詢結果是結構化的。組合中的同樣問題對分解來說恰恰相反。
重命名
清單 13 中的例子把元素 <FirstName> 和 <LastName> 重命名為 <fName> 和 <lName>。對查詢的影響取決於是否直接引用這些元素名。比如 清單 15 按交易找人,因此兩個實例都有結果。如果查詢規定返回 //Person/LastName,就只有第一個實例有結果了。
解決這個問題的一種辦法是為數據庫上層的應用程序建立同義詞表。另一種辦法是在查詢中指定所有的名稱。這種解決方案不夠優雅,而且需要了解模式。清單 16 給出了一個例子。
可選性
這種變化在某些方面與精化和刪除有關。將元素從可選變為必須涉及到舊文檔中沒有實例化的可選元素的精化。比如,將 <SSN> 元素從可選變為必須,對 清單 8 和 9 的影響相同,假設舊實例中沒有給出該元素的值。類似的,將元素從必須變為可選將影響引用原來必須元素的查詢的結果。這種情況下可能出現結果為空。將元素 <birthDate> 從必須改為可選,其後果如 清單 11 所示,假設新實例中沒有給出它的值。因此和精化與刪除有關的問題也適用於這裡。
清單 15. 檢索 TXLifeRequest ID 和人員列表
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id
return <TX>{$tid}
{ for $P in $trans//Person return $P}</TX>
結果
<result>
<TX id="TXLifeRequest1001">
<Person>
<FirstName>Alan</FirstName>
<LastName>Bird</LastName>
<Gender>MALE</Gender>
</Person>
</TX>
<TX id="TXLifeRequest1002">
<Person>
<fName>Carl</fName>
<lName>Devon</lName>
<Gender>MALE</Gender>
</Person>
</TX>
</result>
清單 16. 檢索 TXLifeRequest ID 和人員列表
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id
return <TX>{$tid}
{ for $P in $trans//Person/LastName return $P}
{ for $P in $trans//Person/lName return $P}
</TX>
結果
<result>
<TX id="TXLifeRequest1001">
<LastName>Bird</LastName>
</TX>
<TX id="TXLifeRequest1002">
<lName>Devon</lName>
</TX>
</result>
重新編號
也可以指定元素的基數 — 即在同一個父元素中一個元素出現一次還是多次。從一到多將使結果中出現同一元素的多個實例。從多到一更簡單,僅僅減少了結果的基數。清單 17 所示新文檔實例中允許三個電話號碼,而舊文檔中只有一個。檢索參與者電話號碼的查詢(類似於 清單 12)對舊模式返回一個電話號碼,新模式返回多個電話號碼。
清單 17. 模式變化的文檔實例:改變基數和類型
舊模式的文檔實例
<TXLife>
<TXLifeRequest id="TXLifeRequest1001">
<TransRefGUID>2006-0712-1001</TransRefGUID>
<TransType tc="103">Schema Evolution</TransType>
<OLifE>
<Party id="ID1101">
<Person>
<FirstName>Alan</FirstName>
<LastName>Birch</LastName>
<Gender>MALE</Gender>
<Phone>212 123 4567</Phone>
</Person>
<Address>
<Line1>202 W 101st St</Line1>
<City>New York</City>
<AddressStateTC tc="60">NY</AddressStateTC>
<Zip>10025</Zip>
</Address>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
新模式的文檔實例
<TXLife>
<TXLifeRequest id="TXLifeRequest1002">
<TransRefGUID>2006-0712-1001</TransRefGUID>
<TransType tc="103">Schema Evolution</TransType>
<OLifE>
<Party id="ID1103">
<Person>
<FirstName>Carl</FirstName>
<LastName>Devon</LastName>
<Gender>MALE</Gender>
<Phone>212 234 4567</Phone>
<Phone>212 234 5678</Phone>
<Phone>212 234 6789</Phone>
</Person>
<Address>
<Line1>80 W 109th St</Line1>
<City>New York</City>
<AddressStateTC tc="60">NY</AddressStateTC>
<Zip>10025-2638</Zip>
</Party>
</OLifE>
</TXLifeRequest>
</TXLife>
改變類型
從可比性和對查詢的影響來看,這種變化最有挑戰性,因為要保持查詢仍然有效必須遵循特定的過程。比如,假設 where 子句對值 v 進行整數比較,而後來 v 的類型變成了 string。這種情況下需要強制類型轉換函數(如 $elem cast as xs:integer)來保持查詢的有效性。
這種變化還包括擴展元素和通過刻面限制元素。約束刻面包括 length、minLength、maxLength、pattern、enumeration、whiteSpace、maxInclusive、maxExclusive、minExclusive、minInclusive、totalDigits 和 fractionDigits。從查詢公式的角度來看,這些變化影響帶有比較約束的查詢。此外,受到影響的類型的結果也不同。比如,清單 17 將郵政代碼范型從 <pattern value='[0-9]{5}'/> 變為 <pattern value='[0-9]{5}(-[0-9]{4})?'/> — 即增加了可選的四位數字擴展。檢索元素內容的一半查詢,如 清單 18 所示,沒有什麼問題。另一方面,比較約束很容易破壞查詢,如 清單 19 所示。
默認值
模式驗證為文檔實例中省略的屬性和空白元素添加默認值。如果數據類型相同,改變默認值對查詢公式沒有影響,但是影響這些值的查詢結果。還要特別注意使用比較約束檢索默認值以外的所有數據的查詢。這種情況下對不同模式的文檔應使用不同的查詢訪問文檔。
清單 18. 檢索 TXLifeRequest ID 和地址列表
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id return <TX>{$tid}
{ for $P in $trans//Address return $P}</TX>
結果
<result>
<TX id="TXLifeRequest1001">
<Address><Line1>202 W 101st At</Line1>
<City>New York</City>
<AddressStateTC tc="60">NY</AddressStateTC>
<Zip>10025</Zip></Address>
</TX>
<TX id="TXLifeRequest1003">
<Address> <Line1>80 W 109th St </Line1>
<City>New York</City>
<AddressStateTC tc="60">NY</AddressStateTC>
<Zip>10025-2638</Zip></Address>
</TX>
</result>
清單 19. 檢索郵政編碼為 10025 的 TXLifeRequest ID 地址列表
XQuery
for $trans in //TXLifeRequest
for $tid in $trans/@id
return
<TX>{$tid}
{ for $P in $trans//Address
where Zip="10025"
return $P}
</TX>
結果
<result>
<TX id="TXLifeRequest1001">
<Address> <Line1>202 W 101st St</Line1>
<City>New York</City>
<AddressStateTC tc="60">NY</AddressStateTC>
<Zip>10025</Zip>
</Address>
</TX>
<TX id="TXLifeRequest1003"></TX>
</result>
名稱空間
在 XML 模式演化的過程中,有時候用不同的名稱空間表示不同的模式版本。如果文檔名稱空間和查詢中指定的不同,對名稱空間敏感的查詢將返回空的結果。如果希望返回相同的結果,查詢中的名稱空間應使用通配符。
改變順序
一般來說,多數 XML 查詢對順序不敏感。但是,XML 查詢中有時候使用位置謂詞,比如:
//OLifE/party[2]
這種情況下,查詢可能返回錯誤的結果,對應用程序的功能產生嚴重影響。
管理 XML 模式演化的基本原則
這一節討論控制模式演化的實際問題,並提供出一組用於編寫跨模式版本查詢的指導原則。
控制模式變化
要保證查詢不受模式演化的影響,一個相關問題是如何控制模式演化本身。如何管理 XML 模式演化有很多因素影響,比如:
誰控制模式的變化
誰控制應用程序語義(比如查詢、更新和模式驗證)
允許什麼類型的模式變化
允許什麼類型的應用程序(和查詢)
表 3 列舉了這些問題的不同答案以及相應的例子。
在這四種角色中,XML 數據和應用程序架構師對於如何設計模式變化和應用程序來管理模式演化具有最大的自由度。而與之相反的另一端,一般 XML 用戶對於管理模式演化無能為力。夾在中間的應用程序架構師可以控制如何編寫應用程序中的查詢。一般來說,他們需要確定什麼時候需要中斷應用程序以及如何中斷應用程序中的查詢。應用程序中斷可能有以下幾種情況:
應用程序跨所有版本的 XML 模式工作
大的版本變化導致程序中斷
較小的版本變化導致程序中斷
表 3. 控制應用程序和模式變化的不同選項
類似的,對不同模式執行查詢可能得到下面的結果:
查詢返回正確的結果(正確性由應用程序的語義決定)
查詢沒有結果
查詢返回錯誤的結果
如果應用程序遇到新模式版本的 XML 文檔,應用程序架構師需要決定要哪種結果,以及如何處理這種結果。
一般來說,對一般信息(如消息標識符和社會安全號碼)的查詢應該對所有模式版本都能返回正確的結果。高度依賴重大版本變化的應用程序,應用程序應該不返回結果或者檢查重大版本變化並標記異常。類似的,對小版本變化敏感的應用程序,查詢應該不返回結果或者標記異常。一般應避免不檢查版本變化並允許處理錯誤的結果。
這就提出了什麼是重要和細微的版本變化 — 這個問題部分取決於應用程序語義。但是,因為應用程序需要檢查重要或細微版本變化,架構師需要考慮如何對變動編碼。換句話說,如何編碼版本以及新模式改動是大是小取決於 XML 數據架構師,他需要考慮兩方面的因素。首先是如何編碼模式版本的問題,通常有兩種方法:
用名稱空間編碼版本
在 XML 文檔中包含明確的 version 元素或屬性
每種方法各有長短。
其次,XML 數據架構師需要對 XML 數據的不同應用程序和使用者有所了解(比如如何處理 XML 文檔),以便決定模式的變化是大還是小。
編寫跨越不同模式版本的查詢
本文假設 Web 應用程序或者服務必須支持所有 XML 模式版本,查詢必須對各種版本都能返回正確的結果。在這種情況下,根據前面的示例,我們設計了下列原則以便保證查詢適用於不同的模式版本。
不要在層次的中間增加必須的元素。如果需要,查詢可使用 XPath 中的祖先或後代軸,否則就不能用於新的模式。
類似的,不要從層次結構的中間刪除必須的元素(或屬性)。
如果查詢涉及到順序謂詞,不要改變模式中的元素順序。
如果查詢是強類型的或者需要進行值比較,不要改變原子類型(string、integer 等)。
如果任何查詢引用了元素名稱,不要改變元素名稱。如果必須修改,則考慮在應用程序中增加同義詞控制,保證新舊名稱指向同一個元素。
類似的,不要改變被查詢引用的元素的類型或者刻面。如果必須改變類型,必須同時修改查詢,增加相應的強制類型轉換函數。
如果使用名稱空間區分模式版本,應考慮在查詢中的名稱空間約定中增加通配符(*)。
需要特別注意某些 XPath 函數如 exist ,因為它們要求具有特定元素的版本才能進行查詢計算。這類函數應小心使用。
查詢組合或者分解的元素返回結構不同的結果。一方面,如果組合或分解元素用於比較(在 where 子句中)或者路徑表達式,查詢可能無法用於所有模式版本的文檔實例。審查訪問組合或分解元素的查詢以保證得到適當的結果。
結束語
本文提出了 XML 模式演化過程中變動的擴展分類。我們詳細討論了模式演化對驗證、查詢公式和結果的影響。在此基礎上,我們提出了一組能夠保持跨模式版本查詢的基本原則。本文並沒有介紹所有可能的情形,而是列舉 XML 設計人員在模式演化過程中遇到的最常見的情形。此外,有些情況下,查詢不應該跨大的版本變動工作。本文中沒有涉及到這些情況,將在以後的文章探討。