使用CAM機制提高XML驗證水平
原創【51CTO獨家特稿】CAM即內容裝配機制(Content Assembly Mechanism),它是一個XML結構驗證方法,由于它是一個新生事物,文檔很少,因此本文就當掃盲了。
XML文檔的驗證需要確認文檔是完整的,并且符合在文檔類型定義(Document Type Definition 即DTD)中指定的規則,DTD是最早的規范說明方法,它提供了有用但有限的功能來驗證XML文檔結構,但只有一點語義;接著出現了XML Schema,它提供了更多靈活性和功能,增強了對結構的支持,并且很好地支持了語義,Schematron,RelaxNG已經嘗試提升對語義的支持,但都沒有取得什么進展;現在一種全新的技術在OASIS的保護下開發出來了,它就是CAM。
CAM不僅是一門schema語言,其設計目的是更好地滿足業務交流和互操作性要求,它提供了強有力的機制來驗證XML結構和語義,使其簡潔、易于使用和易于維護;它提供了一個上下文機制 -- 一種基于XML自身其它部分或外部參數來動態調整那些應被視為有效的XML實例。
CAM是一個令人興奮的技術,它的未來充滿希望,但它是一個新技術,可能有好有壞;CAM的開發非常迅速,因此在本文中,你可能會發現很多‘在寫本文的時候’的字眼。開發團隊也很勤奮,你反饋的問題可能很快就會得到修復,而且有些問題可能你還沒有發現就已經被修復了。
因此,在寫本文的時候,CAM文檔還很潦草:有一個正式的規范,一份白皮書,一份PowerPoint演示文稿和一些簡要介紹了編輯器和API的網頁。還沒有明確的指導和教程,本文的目標就是:“CAM:缺少的手冊”,擴大CAM文檔陣營。
你需要
◆基本上熟悉XPath,CAM大量使用了Xpath定義業務規則,請參考w3School的Xpath教程溫習一下。
◆基本上熟悉XML Schema,雖然本文表面上看起來是XML Schema的繼承,因為它廣泛地依賴于與XML Schema的對比,作為最有效的溝通方法,請參考w3School的XML Schema教程溫習一下。
規定合法XML
XML文檔是元素的多層次組合,它是一個用于存儲任何數量文字或數據結構的樹狀存儲結構,XML文檔需要很好的格式,這意味著它只有一個根,其元素和屬性必須符合簡單的XML語法規則,在XML沒有映射到特定的問題域(如數學、書籍協作或金融交易)之前,它都沒什么用處,這種映射將抽象的XML區域以一種專業XML語言與你的特定問題對應起來,任何專用語義都必須事先定義好,否則就會被認為是無效的,遭到拒絕。
思考一下下面的顧客地址:
|
為了在XML Schema中驗證這個XML片段,你通常會定義一個如下的結構:
|
這些限制條件指出﹤address_street﹥元素必須存在,并且必須包含在﹤address﹥元素內,還必須包含一個字符串。對于一個地址而言,一個簡單的字符串值可能是適當的,但其它字段你應該使用更具體的東西,要么是一個專門的字符串(一種衍生的,有限制的字符串)、日期、整數或其它定義類型。
XML Schema是一種基于語法的系統,在它里面你需要同時為語義和結構定義語法;另一方面,Schematron是一個基于規則的系統,你可以使用規則同時指定語義和結構,即你不僅使用規則指定address_street是一個字符串,還用規則指定﹤address_street﹥必須顯示在﹤address﹥元素內,XML Schema和Schematron從根本上說語義和結構都是糾纏在一起的。在編程方面耦合度很高,這是不可取的。
相比之下,CAM是一個混合系統,它將結構從語義中獨立出來(低耦合),使用規則指定語義,例如address示例看起來象:
|
CAM模板的﹤as:Structure﹥小節定義了XML文檔的層次結構,實際上它是從XML文檔示例復制過來的,只不過將真實數據替換成占位符(以百分號標志出來)而已,因此前面的CAM模板表示是一個使用%street number and number%占位符替換真實數據的XML實例。
﹤as:Structure﹥小節補充了部分語義,它定義了一個元素包含了哪些其他元素,以及順序,和Schematron不一樣,你不需要費力地編寫規則代碼定義結構,CAM以所見即所得的形式指定結構,而Schematron不得不自己動手寫代碼,就和使用微軟Word字處理軟件一樣方便,所見即所得形式相對使用RTF文本生成Word文檔而言,編寫RTF實在是太乏味、太困難了,而且容易犯錯,如圖1所示。
![]() |
圖1 |
所見即所得示例,微軟Word使用更直觀的形式編輯文檔(左側),但這兩種方式實際上都在完成同一件事情
即使有一些很酷的工具如XmlSpy或Liquid XML Studio可以幫助你實現所見即所得的感覺,XML Schema也不是所見即所得的,思考一下下面的示例代碼,它定義了一個cost,范圍在1-999,保留2位小數。
|
下面顯示的兩個CAM語法是等同的:
|
CAM模板的﹤as:Rules﹥小節定義了在﹤as:Structure﹥小節中明確嵌入的語義之外的所有語義,包括數據類型、約束、基數、條件等。
CAM的優勢
表1總結了相對于XML Schema和DTD,CAM的關鍵優勢,表這每行將會在本文后面介紹,或以后的文章中介紹。
表1
序號 | 項目 | DTD | XML Schema | CAM | 示例/注釋 |
1 | 隔離結構和業務規則 | 無 | 無 | 有 | |
2 | 當前節點固定驗證 | 無 | 有 | 有 | ﹤quantity﹥將一個整數固定在1到100之間 |
3 | 當前節點條件驗證 | 無 | 受限的 | 有 | ﹤zip﹥必須是5位數或10位數 |
4 | 跨節點條件驗證 | 無 | 受限的 | 有 | 如果﹤state﹥是FL,NV,SD,TX,WA,WY,NH或TN,﹤taxable﹥必須是no,否則就是yes |
5 | 上下文機制 | 無 | 有 | 有 | 依賴于條件A或B是否符合 |
6 | 結構可變性 | 無 | 無 | 有 | 訂購數量超過25kg的顧客必須選擇一種物流運送方式 |
7 | 參數化引用 | 無 | 無 | 有 | 從加拿大采購必須符合條件x、y和z,從新西蘭訂購必須符合條件a、b和c |
8 | 命名空間感知 | 無 | 有 | 有 | |
9 | 定義自己的數據類型 | 不行 | 可以 | 可以 | ﹤bookNumber﹥必須是8位字符串 |
10 | 語法和文檔一樣 | 不行 | 可以 | 可以 | |
11 | 代碼重用 | 受限的 | 可以 | 可以 | ﹤shipTo﹥和﹤billTo﹥地址包含相同的驗證規則 |
12 | 工具/編輯器 | 多 | 多 | 1 | |
13 | 圖形化設計器 | 多 | 多 | 無 | 使用XML Schema設計器時可以設計出復雜的結構 |
14 | 所見即所得 | 使用擴展框架 | 使用擴展框架 | 固有的 | 業務規則語句和它們執行時幾乎是一致的,真正做到了按原文所見即所得, |
15 | 采用情況 | 成熟 | 成熟 | 初生嬰兒 | 成熟的穩定性更好,支持也多 |
16 | API | Java、Perl、Ruby、.Net | Java、Perl、Ruby、.Net | Java | |
17 | 開放標準 | 是 | 是 | 是 |
表 1 重要的驗證特性,DTD,XML Schema和CAM對比表
#p#
CAM編輯器介紹
從http://www.jcam.org.uk/下載最新版本的CAM編輯器,你可以選擇下載CAM模板編輯器或Jcam引擎,本文中大部分地方你只需要CAM模板編輯器就夠了(Jcam引擎執行CAM驗證)。
為了啟動CAM編輯器,你可能需要從零開始或從一個現有的XML文件或XSD文件創建一個模板,你會發現實際上創建一個模板還是瞞簡單的,我們還是使用W3C的Purchase Order模型開始,將這個文件存儲到本地,命名為po.xsd,在編輯器中,選擇‘文件’?‘從模型新建模板’,指定你剛剛存儲的文件的目錄和文件名(如圖2所示),在處理這個文件時程序可能會停頓幾秒鐘,處理完畢后,它會填滿根元素comment區域。
![]() |
圖2 |
從模型新建模板對話框,指定你的XSD文件的路徑和文件名,然后從模型中選擇根元素,以便CAM編輯器為你創建一個基礎的CAM模板
Comment元素是po.xsd文件中所有﹤xsd:element﹥節點的第一個節點(按字母順序)的名字,這個文件包含兩個節點:comment和purchaseOrder。在下面的節選中以粗體顯示。你可以在清單1中查看完整的模型。
﹤xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"﹥
﹤xsd:annotation﹥
﹤xsd:documentation xml:lang="en"﹥
Purchase order schema for Example.com.
Copyright 2000 Example.com. All rights reserved.
﹤/xsd:documentation﹥
﹤/xsd:annotation﹥
﹤xsd:element name="purchaseOrder"
type="PurchaseOrderType"/﹥
﹤xsd:element name="comment" type="xsd:string"/﹥
﹤xsd:complexType name="PurchaseOrderType"﹥
﹤xsd:sequence﹥
﹤xsd:element name="shipTo" type="USAddress"/﹥
﹤xsd:element name="billTo" type="USAddress"/﹥
﹤xsd:element ref="comment" minOccurs="0"/﹥
﹤xsd:element name="items" type="Items"/﹥
﹤/xsd:sequence﹥
﹤xsd:attribute name="orderDate" type="xsd:date"/﹥
﹤/xsd:complexType﹥
...
﹤/xsd:schema﹥
清單1 Po.xsd模型
下面是從w3c獲得了原始po.xsd模型,本文將使用它構建CAM模板:
﹤xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"﹥
﹤xsd:annotation﹥
﹤xsd:documentation xml:lang="en"﹥
Purchase order schema for Example.com.
Copyright 2000 Example.com. All rights reserved.
﹤/xsd:documentation﹥
﹤/xsd:annotation﹥
﹤xsd:element name="purchaseOrder" type="PurchaseOrderType"/﹥
﹤xsd:element name="comment" type="xsd:string"/﹥
﹤xsd:complexType name="PurchaseOrderType"﹥
﹤xsd:sequence﹥
﹤xsd:element name="shipTo" type="USAddress"/﹥
﹤xsd:element name="billTo" type="USAddress"/﹥
﹤xsd:element ref="comment" minOccurs="0"/﹥
﹤xsd:element name="items" type="Items"/﹥
﹤/xsd:sequence﹥
﹤xsd:attribute name="orderDate" type="xsd:date"/﹥
﹤/xsd:complexType﹥
﹤xsd:complexType name="USAddress"﹥
﹤xsd:sequence﹥
﹤xsd:element name="name" type="xsd:string"/﹥
﹤xsd:element name="street" type="xsd:string"/﹥
﹤xsd:element name="city" type="xsd:string"/﹥
﹤xsd:element name="state" type="xsd:string"/﹥
﹤xsd:element name="zip" type="xsd:decimal"/﹥
﹤/xsd:sequence﹥
﹤xsd:attribute name="country" type="xsd:NMTOKEN"
fixed="US"/﹥
﹤/xsd:complexType﹥
﹤xsd:complexType name="Items"﹥
﹤xsd:sequence﹥
﹤xsd:element name="item" minOccurs="0" maxOccurs="unbounded"﹥
﹤xsd:complexType﹥
﹤xsd:sequence﹥
﹤xsd:element name="productName" type="xsd:string"/﹥
﹤xsd:element name="quantity"﹥
﹤xsd:simpleType﹥
﹤xsd:restriction base="xsd:positiveInteger"﹥
﹤xsd:maxExclusive value="100"/﹥
﹤/xsd:restriction﹥
﹤/xsd:simpleType﹥
﹤/xsd:element﹥
﹤xsd:element name="USPrice" type="xsd:decimal"/﹥
﹤xsd:element ref="comment" minOccurs="0"/﹥
﹤xsd:element name="shipDate" type="xsd:date" minOccurs="0"/﹥
﹤/xsd:sequence﹥
﹤xsd:attribute name="partNum" type="SKU" use="required"/﹥
﹤/xsd:complexType﹥
﹤/xsd:element﹥
﹤/xsd:sequence﹥
﹤/xsd:complexType﹥
﹤!-- 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﹥
﹤/xsd:schema﹥
你實際上想要purchaseOrder作為根,因此在對話框中將根元素切換成purchaseOrder,然后點擊‘確定’生成模板,此時程序會提示你保存模板,保存后模板就在CAM模板編輯器中打開了,如圖3所示:
![]() |
圖3 |
CAM編輯器,從po.xsd模型生成模板后,編輯器同時顯示了結構和規則
編輯器中的每個標簽容器都涉及到一個視圖,結構視圖以樹形結構顯示XML的層次,圖3顯示定單有一個orderData屬性和四個子節點:shipTo,billTo,comment和items。items節點可能包括多個item子節點。CAM編輯器精確地反映了基礎XML CAM模板文件(PurchaseOrder/purchaseOrder_from_schema.cam),如下所示,這個文件中的﹤as:AssemblyStructure﹥小節顯示的內容實際上與圖3中結構視圖中的信息是一致的:
﹤as:AssemblyStructure﹥
﹤as:Structure taxonomy="XML" ID="purchaseOrder" reference=""﹥
﹤purchaseOrder orderDate="%YYYY-MM-DDZ%"﹥
﹤shipTo country="US"﹥
﹤name﹥%string%﹤/name﹥
﹤street﹥%string%﹤/street﹥
﹤city﹥%string%﹤/city﹥
﹤state﹥%string%﹤/state﹥
﹤zip﹥%54321.00%﹤/zip﹥
﹤/shipTo﹥
﹤billTo country="US"﹥
﹤name﹥%string%﹤/name﹥
﹤street﹥%string%﹤/street﹥
﹤city﹥%string%﹤/city﹥
﹤state﹥%string%﹤/state﹥
﹤zip﹥%54321.00%﹤/zip﹥
﹤/billTo﹥
﹤comment﹥%string%﹤/comment﹥
﹤items﹥
﹤item partNum="%string%"﹥
﹤productName﹥%string%﹤/productName﹥
﹤quantity﹥%1%﹤/quantity﹥
﹤USPrice﹥%54321.00%﹤/USPrice﹥
﹤comment﹥%string%﹤/comment﹥
﹤shipDate﹥%YYYY-MM-DDZ%﹤/shipDate﹥
﹤/item﹥
﹤/items﹥
﹤/purchaseOrder﹥
﹤/as:Structure﹥
﹤/as:AssemblyStructure﹥
相比之下,XSD文件混合了結構和業務規則,因此維護成本更高,下面是一個完整CAM文件的頂層框架,顯示了兩個主要的元素:
﹤as:CAM
xmlns:as="http://www.oasis-open.org/committees/cam"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:camed="http://jcam.org.uk/editor"
CAMlevel="1"
version="1.0"﹥
﹤as:Header /﹥
﹤as:AssemblyStructure /﹥
﹤as:BusinessUseContext /﹥
﹤/as:CAM﹥
你可以在清單2中查看完整的CAM模板文件。
清單2 生成的CAM模板
從原始XML模型文件生成的purchaseOrder_from_schema.cam模板:
﹤as:CAM xmlns:as="http://www.oasis-open.org/committees/cam"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:camed="http://jcam.org.uk/editor" CAMlevel="1" version="1.0"﹥
﹤as:Header﹥
﹤as:Description﹥Generated for : purchaseOrder﹤/as:Description﹥
﹤as:Owner﹥To be Completed﹤/as:Owner﹥
﹤as:Version﹥0.1 generator v1.18﹤/as:Version﹥
﹤as:DateTime﹥2008-12-08T12:31:34﹤/as:DateTime﹥
﹤/as:Header﹥
﹤as:AssemblyStructure﹥
﹤as:Structure taxonomy="XML" ID="purchaseOrder" reference=""﹥
﹤purchaseOrder orderDate="%YYYY-MM-DDZ%"﹥
﹤shipTo country="US"﹥
﹤name﹥%string%﹤/name﹥
﹤street﹥%string%﹤/street﹥
﹤city﹥%string%﹤/city﹥
﹤state﹥%string%﹤/state﹥
﹤zip﹥%54321.00%﹤/zip﹥
﹤/shipTo﹥
﹤billTo country="US"﹥
﹤name﹥%string%﹤/name﹥
﹤street﹥%string%﹤/street﹥
﹤city﹥%string%﹤/city﹥
﹤state﹥%string%﹤/state﹥
﹤zip﹥%54321.00%﹤/zip﹥
﹤/billTo﹥
﹤comment﹥%string%﹤/comment﹥
﹤items﹥
﹤item partNum="%string%"﹥
﹤productName﹥%string%﹤/productName﹥
﹤quantity﹥%1%﹤/quantity﹥
﹤USPrice﹥%54321.00%﹤/USPrice﹥
﹤comment﹥%string%﹤/comment﹥
﹤shipDate﹥%YYYY-MM-DDZ%﹤/shipDate﹥
﹤/item﹥
﹤/items﹥
﹤/purchaseOrder﹥
﹤/as:Structure﹥
﹤/as:AssemblyStructure﹥
﹤as:BusinessUseContext﹥
﹤as:Rules﹥
﹤as:default﹥
﹤as:context﹥
﹤as:constraint action="makeOptional(//purchaseOrder/@orderDate)" /﹥
﹤as:constraint condition="string-length(.) ﹤11"
action="setDateMask(//purchaseOrder/@orderDate,YYYY-MM-DD)" /﹥
﹤as:constraint condition="string-length(.) ﹥10"
action="setDateMask(//purchaseOrder/@orderDate,YYYY-MM-DDZ)" /﹥
﹤as:constraint action="makeOptional(//shipTo/@country)" /﹥
﹤as:constraint action="datatype(//shipTo/@country,NMTOKEN)" /﹥
﹤as:constraint action="setNumberMask(//shipTo/zip,######.##)" /﹥
﹤as:constraint action="makeOptional(//billTo/@country)" /﹥
﹤as:constraint action="datatype(//billTo/@country,NMTOKEN)" /﹥
﹤as:constraint action="setNumberMask(//billTo/zip,######.##)" /﹥
﹤as:constraint action="makeOptional(//purchaseOrder/comment)" /﹥
﹤as:constraint action="makeRepeatable(//items/item)" /﹥
﹤as:constraint action="makeOptional(//items/item)" /﹥
﹤as:constraint action="setNumberMask(//item/quantity,######)" /﹥
﹤as:constraint action="setNumberRange(//item/quantity,1-999999)" /﹥
﹤as:constraint action="setNumberMask(//item/USPrice,######.##)" /﹥
﹤as:constraint action="makeOptional(//item/comment)" /﹥
﹤as:constraint action="makeOptional(//item/shipDate)" /﹥
﹤as:constraint condition="string-length(.) ﹤11"
action="setDateMask(//item/shipDate,YYYY-MM-DD)" /﹥
﹤as:constraint condition="string-length(.) ﹥10"
action="setDateMask(//item/shipDate,YYYY-MM-DDZ)" /﹥
﹤/as:context﹥
﹤/as:default﹥
﹤/as:Rules﹥
﹤/as:BusinessUseContext﹥
﹤/as:CAM﹥
規則視圖(圖3中高亮顯示)顯示了所有的業務規則,組成了模板的語義,與結構不同,規則存儲在文件中時與規則視圖不一樣,表2將規則視圖中的規則集中在一起了,在沒有研究這些規則的詳細情況時,你可以從它們發現:
◆規則可能是有條件的或絕對的,例如orderDate依賴于它的長度格式要求改變。
◆項目和條件是通過XPath指定的,在CAM中會廣泛使用到XPath,它提供了極大的靈活性和清晰度,相比之下,XML Schema 1.0只為高級的xs:unique和xs:key concepts使用XPath。
◆規則可能適用于很廣的元素范圍,也可能只能適用于很少的元素,XPath支持選擇文檔的中任何一部分:一個元素、一個屬性、所有你給定名稱的元素、所有在樹中確定位置的元素等。
◆規則是壓縮的、簡潔的、非常直觀的。實際上,正如你將會看到的,編寫CAM規則和編寫應用程序需求是一樣的。
條件 | 項目 | 行為 |
//purchaseOrder/@orderDate | makeOptional() | |
string-length(.) ﹤ 11 | //purchaseOrder/@orderDate | setDateMask(YYYY-MM-DD) |
string-length(.) ﹥ 10 | //purchaseOrder/@orderDate | setDateMask(YYYY-MM-DDZ) |
//shipTo/@country | makeOptional() | |
//shipTo/@country | datatype(NMTOKEN) | |
//shipTo/zip | setNumberMask(######.##) | |
//billTo/@country | makeOptional() | |
//billTo/@country | datatype(NMTOKEN) | |
//billTo/zip | setNumberMask(######.##) | |
//purchaseOrder/comment | makeOptional() | |
//items/item | makeRepeatable() | |
//items/item | makeOptional() | |
//item/quantity | setNumberMask(######) | |
//item/quantity | setNumberRange(1-999999) | |
//item/USPrice | setNumberMask(######.##) | |
//item/comment | makeOptional() | |
//item/shipDate | makeOptional() | |
string-length(.) ﹤ 11 | //item/shipDate | setDateMask(YYYY-MM-DD) |
string-length(.) ﹥ 10 | //item/shipDate | setDateMask(YYYY-MM-DDZ) |
表 2 編輯器中的業務規則:為定單轉換XML Schema,讓CAM自動生成這些規則。
#p#
CAM驗證示例
現在你可以使用手中的模板驗證XML文件,W3C網站上除了提供定單模型外,還提供了一個定單實例(PurchaseOrder/po.xml),但下載下來的會有一個印刷錯誤,圖4高亮顯示了錯誤,如果你嘗試打開或驗證畸形的XML文件,CAM編輯器會顯示堆棧轉儲信息和錯誤消息(也看圖4),并拒絕載入文件。
![]() |
圖4 |
畸形XML文件:這個圖顯示了為什么原始的po.xml文件不是合適的,將其載入CAM編輯器時顯示出其錯誤
當你通過將感嘆號和左半邊尖括號對換位置修復這個錯誤后(正確的文件是PurchaseOrder/po_corrected.xml),你可以使用CAM編輯器載入這個XML文件,CAM編輯器以XML視圖形式顯示這個文件,繪制成如結構視圖那樣的樹狀結構,如圖5所示,目前在模板中相同的元素顯示的是真實的值而不是占位符。
![]() |
圖5 |
XML視圖:當你打開一個XML文件時,以XML視圖形式顯示樹形結構,可以折疊和展開
為了驗證文檔,選擇‘運行’?‘運行JCam’,你將會看到如圖6所示的Jcam運行對話框,默認情況下,Jcam選擇載入的XML文件,應該可以通過它的結構ID如purchaseOrder(這個結構的根)來識別,點擊‘完成’關閉這個對話框開始驗證,結果顯示在主窗口中下方的運行結果視圖中,注意驗證過程發現了兩個錯誤,盡管在圖6中只顯示了一個,如果你仔細一看,你會發現有錯誤的節點上會有一個黃色或紅色的圖標,在本例中,錯誤發生在﹤zip﹥元素上,它的父元素﹤shipTo﹥也顯示了一個錯誤圖標,甚至根元素﹤purchaseOrder﹥也顯示了一個錯誤圖標。同樣,你可以推斷第二個錯誤是隱藏在﹤billTo﹥元素中的。
![]() |
圖6 |
執行驗證:驗證結果顯示在運行結果視圖中,每個驗證失敗的元素或屬性都有一個錯誤標記,它的上級元素就有一個警告標記
這個XML文件在任何XML Schema編輯器中驗證都沒有錯誤,為什么在這里驗證就失敗了呢?運行結果視圖中的錯誤指出zip代碼根據CAM模板的定義是無效的,這個模板會檢查是否是一個浮點數,因為在美國zip代碼要么是5位要么是9位的整數,zip代碼的CAM模板規則來自XSD規格說明XSD規格說明簡單說明了zip代碼是一個十進制數,這一點你可以從清單1中看到:在USAddress復雜類型中查找zip字段,CAM模板生成程序應該避免不用的輸入輸出。但你可能不同意,我提交的XSD規格說明太寬松了,數據類型應該是一個整數而不是一個十進制數,下面的部分將會介紹如何使用CAM編輯器來糾正這個錯誤。
當你按照本文的例子進行研究時,你可能會遇到模板沒有象預期那樣運轉,在這種時候要檢查兩樣東西:
◆點擊‘工具’→‘驗證CAM模板’菜單項查找所有問題。
◆如果你在運行JCam對話框中點擊‘完成’按鈕,似乎什么事情都不會發生,按‘取消’關閉對話框,然后查看控制臺視圖中的錯誤消息,例如,如果你忘記指定要驗證的XML文件了,對話框不會禁用完成按鈕,控制臺視圖中報告的錯誤是‘模板是空的’,這多少會讓人有些誤解。如果控制臺視圖什么都沒有顯示,那就表示一切ok。
#p#
創建業務規則
在結構視圖中選擇﹤shipTo﹥元素下的﹤zip﹥元素,附加到這個元素的規則顯示在項目規則(ItemRules)視圖中,在本例中只有一個規則,使用的是setNumberMask謂詞。在類別(category)列中的規則上點擊右鍵打開這個規則的上下文菜單,然后選擇‘編輯規則’,打開編輯約束規則對話框,如圖7所示。
![]() |
圖7 |
編輯約束規則:為了修復setNumberMask謂詞附加到//shipTo/zip元素,選擇結構視圖中的元素,打開它的上下文菜單,選擇編輯規則打開編輯約束規則對話框,點擊數字特征碼字段明確指定特征碼
在數字特征碼字段上點擊,打開另一個對話框編輯特征碼,現在只需要將######.##修改為#####即可,關閉這兩個對話框,在主編輯器窗口中,你會看到更新后的規則,重新執行一次驗證,//shipTo/zip錯誤應該不會再出現,只留下//billTo/zip錯誤,很明顯這是一個相同的錯誤,因此你可以使用相同的手段修復它,但因為//billTo/zip和//shipTo zip的值應該一樣,這樣就可以使用一個通用的規則而不用每個指定一條規則了,本文的第二部分將會詳細地介紹如何使用通用規則。
規則更新后你也應該更新占位符(圖7中的項目1),如果你和圖6比較,你會發現值從%54321.00%變成%54321%了,它更能代表zip代碼,在這個特殊的例子中,元素的占位符和關聯的規則的緊密相關的,假設它們自動相互跟蹤是合理的,但在許多情況下,關系并不是直接的,元素和規則是多對多的關系:你可以對一個元素應用多個規則,或者一個規則應用給多個元素。
為了更新圖7所示的元素占位符,在結構視圖中//shipTo/zip字段上打開上下文菜單,選擇‘編輯文本’,在對話框中將54321.00%修改成%54321%。
占位符為兩個角色服務,CAM處理程序單獨使用它確定某個元素的內容是否已被修復,這是由圍繞在占位符兩邊的百分比符號確定的,注意在更新元素的占位符前,你要重新驗證//shipTo/zip字段,確認在百分比符號中間的值被CAM處理程序忽略。
百分比符號之間的值應該是準確、簡明地指出包含什么元素,通常上下文已經為你完成了大部分工作:元素的名字是‘zip’,在美國它會被立即認為是5、9或10位整數,通過設置占位符為%54321%,你告訴用戶模板只接受5個字符的zip代碼。
強度測試驗證
現在你已經更新了占位符和規則,但只修改這兩個地方就足夠驗證zip代碼了嗎?為了測試它,你需要為CAM處理程序提供不同的測試用例,最簡單的方法是打開包含你要驗證的數據的XML視圖,修改//shipTo/zip的值,然后再重新驗證,你可以在XML視圖中象結構視圖那樣編輯節點:打開上下文菜單選擇編輯文本,確定最小值以便覆蓋到所有范圍,讓每個值都被驗證一次,表3提供了這樣一個列表,從這個表中可以看到有兩個地方一個是pass掉,而另一個卻沒有過,只有這兩個驗證函數都過了才行。
//shipTo/zip | setNumberMask(#####) | setStringMask(00000) |
90952 | Pass | Pass |
90952.1 | Fail | Fail |
123456 | Fail | Fail |
90952-1234 | Fail | Fail |
1 | Pass | Fail |
(blank entry) | Fail | Fail |
90952a | Fail | Fail |
-12345 | Pass | Fail |
(123) | Fail | Fail |
表 3 zip代碼測試用例:這個表顯示了使用數字型特征碼#####和使用字符串型特征碼00000進行驗證的結果,結果以綠色表示的是正確的,以紅色表示的是錯誤的
這兩個測試都通過具有相同的原因:特征碼是數字,這兩個測試都是有效的數字。雖然zip代碼只包含數字,實際上它是一個字符串,從數字上來說,00001和1是相等的,在zip代碼域中,00001代表一個有效的代碼,而1不是。因此要使用numeric特征碼代替textual特征碼,為//shipTo/zip打開編輯約束對話框,將行為從setNumberMask修改成setStringMask,在String Mask字段上點擊打開特征碼編輯器,輸入5個0,或者按數字0-9之間任何一個數字5次,然后退出這兩個對話框,如果你現在重新驗證表3中的每個測試用例,你會發現它們所有的測試結果都是正確的。
在正式的CAM規格說明書的3.4.3節(CAM內容特征碼語法)列出了有效的特征碼字符,表4就是改變自它的。
表 4 特征碼字符:當規則行為需要特征碼時,這些字符有特定的含義
字符 | 描述 | |
字符串特征碼 | ||
X | 任何字符,強制性 | |
A | 強制性字母數字字符或空格 | |
a | 非強制性字母數字字符或空格 | |
? | 任何單一字符 | |
* | 0或更多字符0 | |
U | 一個可以被轉換為大寫的字符 | |
^ | 大寫,非強制 | |
L | 一個可以被轉換為小寫的字符 | |
_ | 小寫,非強制 | |
0 | 數字、后綴和前端插入的0,前端插入的正負號 | |
# | 數字、后綴和前端插入的0被取消,前端插入的正負號 | |
' ' | 轉義字符 | |
數字型特征碼 | ||
0 | 數字、后綴和前端插入的0,前端插入的正負號 | |
# | 數字、后綴和取消前端插入的0,前端插入的正負號 | |
. | 小數點 | |
J | 特征碼的第一個字符,可能調用Java格式化方法處理特征碼,當傳遞給Java時,文字J被忽略 | |
日期型特征碼 | ||
DD | 一月中的某天 | |
DDD | 一年中的某天 | |
DDDD | 一月中的相對某天 | |
MM | 一年中的月份 | |
MMM... | 月名,如January(一月),字段會被填充或截斷成3-10個數字 | |
YY | 兩位數的年 | |
YYYY | 四位數的年 | |
W | 一周的某天 | |
WWW... | 期名,字段會被填充或截斷成3-10個數字 | |
/ | 斜號,日期分隔符 | |
- | 連字符,另一個日期分隔符 |
如果你想找一個具有完整、清晰文檔,并且所有的bug都被消除的工具,那這個可能會讓你失望,但你如果不介意寶石周圍那一點點瑕疵,我相信在你的兵器庫中CAM會是一個偉大的工具,最后,我要告訴那些熱心的開發者們,CAM編輯器和CAM引擎最近的版本(我用的是1.6.2版)中的行為完全是合理的。
這是第一部分的內容,到此就結束了,現在你至少知道其實使用CAM設計時還是很簡單的,在本文的第二部分中,你將會看到CAM的強大之處,另外,你還將會看到關于開發模板和規則使用的技術更深入的討論,包括:通用結構和通用規則,基于內部或外部因子的條件驗證,詳細比較XSD關于數據類型、排序和基數。最后還將介紹如何避免常犯的錯誤。
【編輯推薦】