【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語言與你的特定問題對應起來,任何專用語義都必須事先定義好,否則就會被認為是無效的,遭到拒絕。
思考一下下面的顧客地址:
﹤address﹥
﹤address_street﹥221B Baker Street﹤/address_street﹥
. . .
﹤/address﹥
為了在XML Schema中驗證這個XML片段,你通常會定義一個如下的結構:
﹤xs:element name="address"/﹥
. . .
﹤xs:element name="address_street" type="xs:string"/﹥
. . .
﹤/xs:element﹥
這些限制條件指出﹤address_street﹥元素必須存在,並且必須包含在﹤address﹥元素內,還必須包含一個字符串。對於一個地址而言,一個簡單的字符串值可能是適當的,但其它字段你應該使用更具體的東西,要麼是一個專門的字符串(一種衍生的,有限制的字符串)、日期、整數或其它定義類型。
XML Schema是一種基於語法的系統,在它裡面你需要同時為語義和結構定義語法;另一方面,Schematron是一個基於規則的系統,你可以使用規則同時指定語義和結構,即你不僅使用規則指定address_street是一個字符串,還用規則指定﹤address_street﹥必須顯示在﹤address﹥元素內,XML Schema和Schematron從根本上說語義和結構都是糾纏在一起的。在編程方面耦合度很高,這是不可取的。
相比之下,CAM是一個混合系統,它將結構從語義中獨立出來(低耦合),使用規則指定語義,例如address示例看起來象:
﹤as:Structure﹥
﹤address﹥
﹤address_street﹥%street number and name%﹤/address_street﹥
. . .
﹤/address﹥
﹤/as:Structure﹥
﹤as:Rules﹥
﹤as:constraint action="datatype(//address_street,string)" /﹥
﹤/as:Rules﹥
CAM模板的﹤as:Structure﹥小節定義了XML文檔的層次結構,實際上它是從XML文檔示例復制過來的,只不過將真實數據替換成占位符(以百分號標志出來)而已,因此前面的CAM模板表示是一個使用%street number and number%占位符替換真實數據的XML實例。
﹤as:Structure﹥小節補充了部分語義,它定義了一個元素包含了哪些其他元素,以及順序,和Schematron不一樣,你不需要費力地編寫規則代碼定義結構,CAM以所見即所得的形式指定結構,而Schematron不得不自己動手寫代碼,就和使用微軟Word字處理軟件一樣方便,所見即所得形式相對使用RTF文本生成Word文檔而言,編寫RTF實在是太乏味、太困難了,而且容易犯錯,如圖1所示。
所見即所得示例,微軟Word使用更直觀的形式編輯文檔(左側),但這兩種方式實際上都在完成同一件事情
即使有一些很酷的工具如XmlSpy或Liquid XML Studio可以幫助你實現所見即所得的感覺,XML Schema也不是所見即所得的,思考一下下面的示例代碼,它定義了一個cost,范圍在1-999,保留2位小數。
﹤xs:element name="cost"﹥
﹤xs:simpleType﹥
﹤xs:restriction base="xs:decimal"﹥
﹤xs:fractionDigits value="2" /﹥
﹤xs:totalDigits value="5" /﹥
﹤xs:minInclusive value="1" /﹥
﹤xs:maxInclusive value="999" /﹥
﹤/xs:restriction﹥
﹤/xs:simpleType﹥
﹤/xs:element﹥
下面顯示的兩個CAM語法是等同的:
﹤as:constraint action="setNumberMask(//Part/cost,###.##)" /﹥
﹤as:constraint action="setNumberRange(//Part/cost,1-999)" /﹥
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對比表
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自動生成這些規則。
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。
創建業務規則
在結構視圖中選擇﹤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個數字 / 斜號,日期分隔符 - 連字符,另一個日期分隔符 br>如果你想找一個具有完整、清晰文檔,並且所有的bug都被消除的工具,那這個可能會讓你失望,但你如果不介意寶石周圍那一點點瑕疵,我相信在你的兵器庫中CAM會是一個偉大的工具,最後,我要告訴那些熱心的開發者們,CAM編輯器和CAM引擎最近的版本(我用的是1.6.2版)中的行為完全是合理的。
這是第一部分的內容,到此就結束了,現在你至少知道其實使用CAM設計時還是很簡單的,在本文的第二部分中,你將會看到CAM的強大之處,另外,你還將會看到關於開發模板和規則使用的技術更深入的討論,包括:通用結構和通用規則,基於內部或外部因子的條件驗證,詳細比較XSD關於數據類型、排序和基數。最後還將介紹如何避免常犯的錯誤。