在 上一篇文章中,我相當完整地概述了 RELAX NG 模式的語法和語義。然而,有一些問題沒有觸及到,這裡值得對這些問題做進一步的討論。
DTD 和 W3C XML Schema 都允許 信息集增加,而 RELAX NG 卻不行。RELAX NG(以及其它一些廣泛使用的 XML 工具)的創建者之一 James Clark 強烈認為信息集增加違反了 XML 實例文檔和模式角色中的模塊性。換句話說,在 Clark 看來,RELAX NG 所擁有的特性恰是 DTD 和 W3C Schema 的錯誤所在。我對這一問題的感覺是復雜的,但我能理解他的直覺。
讓我們先稍稍回顧一下,看看信息集內容是關於什麼的。基本上,您可以詢問 XML 實例包含什麼數據。如果解析實例而不進行驗證,則結果僅取決於實例的屬性和元素體中有什麼值。如果注重模塊性,則模式將只告訴您實例是否有效;它不會改變文檔中的實際 信息。然而,如果用 DTD 或 W3C XML Schema 來驗證,則違反了這種模塊性。例如,考慮下面這個 DTD:
清單 1. curious.dtd
<!ELEMENT foo EMPTY>
<!ATTLIST foo bar CDATA "curious"
baz CDATA #FIXED "curiouser">
以及這個 XML 實例:
清單 2. curious.XML
<?XML version="1.0"?>
<!DOCTYPE foo SYSTEM "curious.dtd">
<foo/>
非驗證解析器在這個文檔中找到的信息集與驗證解析器找到的不一樣。以下是非驗證實用程序 xmlcat 與驗證實用程序 4XML(兩者都將所遇到的任何東西回送到控制台)的對比:
清單 3. 使用驗證解析器和非驗證解析器的信息集
% ./xmlcat curious.XML
<?XML version="1.0" encoding="iso-8859-1"?>
<foo></foo>
% 4xml -p curious.XML
<?XML version="1.0" encoding="utf-8"?>
<foo bar="curious" baz="curiouser"/>
在 W3C Schema 中, default 和 fixed 屬性對於 <xsd:attribute> 和 <xsd:element> 標記起著類似的作用。
支持使用 default 的理由是它可以使 XML 實例達到最少。正是由於此目的,我使用了 default 屬性(或者可能是 #FIXED 屬性)。但如果恰好本地 XML 文檔的內容依賴於遠程 URI(有可能是虛假的 URI),甚至依賴於在解析期間不會出現網絡中斷,那麼我也會看到危害:導致問題出現以及增加調試難度。
RELAX NG 不執行任何信息集增加。唔,差不多 — 我認為 Clark 誇大了這一點。如果將數據類型強加給元素或屬性,則仍然需要用一種重要的方法來更改值的內容。字符串值“1.0”不同於浮點值“1.0”,即使在 XML 實例中兩者是用完全相同的方法來表示的。
聲明基數
W3C XML Schema 用於表示所需出現的基數的方式比 DTD 或 RELAX NG 模式更簡潔。如果希望 <foo> 元素在 <bar> 元素內出現 5 到 30 次,那麼可以在 W3C Schema 中用一條簡單的規則聲明這一點:
清單 4. W3C XML Schema 基數規則<xsd:element name="bar">
<xsd:element name="foo" minOccurs="5" maxOccurs="30"/>
</xsd:element>
也 可以用 DTD 來聲明同樣的基數規則,但是 十分笨拙:
清單 5. 基數規則<!ELEMENT bar
(foo, foo, foo, foo, foo, foo?,foo?,foo?,foo?,foo?
foo?,foo?,foo?,foo?,foo?,foo?,foo?,foo?,foo?,foo?
foo?,foo?,foo?,foo?,foo?,foo?,foo?,foo?,foo?,foo?) >
我希望 RELAX NG 有一個顯式的 <cardinality> 標記,這樣可以(假想地)編寫如下規則:
清單 6. 假想的 RELAX NG 2.0 基數規則<element name="bar" XMLns="http://relaxng.org/ns/structure/1.0>
<cardinality min="5" max="30">
<element name="foo"/>
</cardinality>
</element>
遺憾的是,在 RELAX NG 的當前版本中,您只能使用 <zeroOrMore> 、 <oneOrMore> 和 <optional> 基數。然而,使用所指定的模式至少可使基數的拼寫略微簡潔些。例如,以下是緊湊的語法:
清單 7. 實際的 RELAX NG 緊湊語法基數規則start = element bar { fivefoo, upto25foo }
fivefoo = element foo { empty }, element foo { empty },
element foo { empty }, element foo { empty },
element foo { empty }
maybefoo = element foo { empty }?
upto25foo =
fivefoo?, fivefoo?, fivefoo?, fivefoo?,
maybefoo, maybefoo, maybefoo, maybefoo, maybefoo
我承認這種命名形式不完美,但至少可以通過重復指定模式來有效地提高效力,從而指定大量基數。
轉換和驗證
有許多工具可以使用 RELAX NG 模式。這些工具主要是用 Java 語言實現的,但有些工具和庫是用 Python、C# 和 Visual Basic 編寫的。令人吃驚的是,我還沒發現用其它語言(譬如 Perl、Ruby 或 C/C++)編寫的任何庫,這些語言似乎很適合。
一類很明顯的 RELAX NG 應用程序就是驗證程序。就象那些使用 DTD 或 W3C XML Schema 的驗證解析器一樣,也有許多命令行、在線和庫解析器可用於 RELAX NG。一類較不明顯的應用程序是使各模式互相轉換的工具。Sun 的 RELAX NG Converter 和 James Clark 的 trang 與 DTDinst使您可以在 RELAX NG(XML 和緊湊語法)、DTD 和 W3C XML Schema 之間互相轉換。我打算在本專欄的下一篇文章編寫一個普通的 Python 工具( compact2XML.py),它使 4Suite 和 xvif能利用 RELAX NG 的緊湊語法(這兩個實用程序的作者都表示有意包括這樣的工具)。
轉換值得我們做更進一步地探討。 第 1 部分從幾個方面討論了 RELAX NG 確實比 W3C XML Schema 功能強大,對一些最有效的轉換所進行的討論說明了這一點。例如,在上一篇文章中提到了一個用於圖書館 patron 的模式,這裡用緊湊語法表示為:
清單 8. 圖書館 patron 緊湊語法element patron {
element name { text } &
element id-num { text } &
element book {
attribute isbn { text } |
attribute title { text }
}*
}
關於用 XML 語法所表示的圖書館,請參閱 第 1 部分,在語義上,它倆是一樣的,但 XML 方法較冗長。在將此轉換成 W3C XML Schema 方面,trang 進行了很好的嘗試。輸入和輸出文件的文件擴展名用於猜測類型(或者用開關來覆蓋):
清單 9. 將 RELAX NG 轉換成 W3C XML Schema% Java -jar trang.jar patron.rnc patron.xsd
% cat patron.xsd
<?XML version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualifIEd" version="1.0">
<xsd:element name="patron">
<xsd:complexType>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="name"/>
<xsd:element ref="id-num"/>
<xsd:element ref="book"/>
</xsd:choice>
</xsd:complexType>
</xsd:element>
<xsd:element name="name">
<xsd:complexType mixed="true"/>
</xsd:element>
<xsd:element name="id-num">
<xsd:complexType mixed="true"/>
</xsd:element>
<xsd:element name="book">
<xsd:complexType>
<xsd:attribute name="isbn"/>
<xsd:attribute name="title"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
這要歸功於 trang,我認為,對於這種情形,這個 W3C XML Schema 是最適合的。RELAX NG 模式接受的 每個 XML 實例,W3C XML Schema 同樣也接受,而且兩者都可以拒絕許多錯誤。問題在於,有一類獨特的 XML 實例,根據所要求的規則,它們 實際上不是有效的,但是卻可以通過 W3C Schema 的驗證。例如:
清單 10. W3C XML Schema 識別的局限性% cat patron-i1.XML
<?XML version="1.0" encoding="UTF-8"?>
<patron>
<book isbn="0-528-84460-X"/>
<name>John Doe</name> <!-- repeats name subelement -->
<name>Second Name</name>
<id-num>12345678</id-num>
<book title="Why RELAX is Clever"/>
</patron>
% cat patron-i2.XML
<?XML version="1.0" encoding="UTF-8"?>
<patron>
<name>John Doe</name>
<id-num>12345678</id-num>
<!-- Too many and too few attributes of book element -->
<book title="Why RELAX is Clever" isbn="0-528-84460-X"/>
<book/>
</patron>
% cat patron-i3.XML
<?XML version="1.0" encoding="UTF-8"?>
<patron/> <!-- No required subelements -->
當然,即使上面這三個示例的驗證都失敗,但 W3C XML Schema 仍能拒絕帶有完全不允許的元素/屬性的 XML 實例,或者拒絕以不正確方式嵌套元素(例如, <book> 嵌套在 <name> 中,而不是作為 <name> 元素的兄弟元素)的 XML 實例。
就驗證工具的作用而言,我發現在驗證失敗時, jing工具能產生有用的錯誤消息,它在這方面做得很好。Python XML 庫 4Suite 合並了 xvif 庫的一個版本,它也可以執行驗證。但比較這些錯誤:
清單 11. 使用 jing 的驗證錯誤消息% Java -jar ../trang/jing.jar patron.rng patron-i3.XML
Error at URL "file:/.../patron-i3.XML",
line number 2: unfinished element
% Java -jar ../trang/jing.jar patron.rng patron-i1.XML
Error at URL "file:/.../patron-i1.XML",
line number 5: element "name" not allowed in this context
清單 12. 使用 4Suite 的驗證錯誤消息% 4xml --rng=patron.rng patron-i1.XML
Traceback (most recent call last):
...
File "/.../site-packages/Ft/Xml/_4XML.py", line 89, in Run
raise RngInvalid(result)
Ft.XML.Xvif.RngInvalid: Qname {None}name not exected
% 4xml --rng=patron.rng patron-i3.XML
Traceback (most recent call last):
...
Ft.XML.Xvif.RngInvalid
當然,在應用程序環境中,將首先考慮選擇能利用這些庫的編程語言,而後才考慮所生成的消息中的差異。
經過編譯的驗證程序
除了 RELAX NG 環境之外,我還沒有仔細研究過的一類工具是單模式驗證程序。請查看 RELAX NG 主頁,其中包含至這些工具(包括 Bali 和 RelaxNGCC)的鏈接。這些框架自動地編寫出針對特定 RELAX NG 模式的專用驗證代碼。可以想象,這樣的專用驗證程序的運行速度一般比通用驗證程序快。這樣的工具是有可能的 — 或者就同一件事,相對於 W3C XML Schema 來說,至少要更簡單 — 因為 RELAX NG 的設計非常好地基於算法分析之上。
經過 RELAX NG 增強的 XML 編輯器
遺憾的是,XML 編輯器對 RELAX NG 的支持還不象對 W3C XML Schema 的支持那樣廣泛。當然,對 DTD 的支持仍然比對其它兩種模式類型的支持更廣泛。這是一個遺憾,其原因是由於 RELAX NG 驗證簡單的概念框架,在編輯器中包含關於 RELAX NG 的定制實際上會 更容易。理想情況下,定制的 XML 編輯器將利用 RELAX NG 模式來指導和幫助用戶,用保證有效性的方式插入屬性和元素。
一種折衷辦法是,使用如 trang 這樣的工具,將 RELAX NG 模式轉換成與其相近的 W3C XML Schema 或 DTD,然後在 GUI XML 編輯器中使用它們。但這樣做只能在有限的范圍內有所幫助。
有一個圍繞 RELAX NG 構建的 XML 編輯器,這是一個基於 Java 技術的 XML Operator。我“擺弄”過它一陣,發現它可能很有用,但它將歸為我以前所討論過的低端 XML 編輯器;XML Operator 只在某些方面實現了幾個功能,它既沒有提供 XML Spy 所具有的大量工具,也不具備 oXygen 所擁有的簡潔性。
下次見
在第 1 部分和第 2 部分(本文)中,我討論了 RELAX NG 的大部分內容,還對使用 RELAX NG 的工具進行了總結。第 3 部分(也就是最後一部分)將簡要討論 RELAX NG 如何讓您在模式中包含外部模式,以及有選擇地合並不同模式的規范。但第 3 部分主要將詳細地研究 RELAX NG 緊湊語法,以及說明緊湊語法和 XML 語法之間的確切對應。