DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> XForms 驗證程序揭密
XForms 驗證程序揭密
編輯:XML詳解     

我撰寫的 XForms 書籍(請參閱 Resources)於 2003 年秋同時在書架上和網絡上出現。在很短的時間內,我收到了大量電子郵件,都是關於 XForms 問題的,通常還附有一兩頁漏洞百出的 XML 源文件。一般情況下,我是樂於答復電子郵件的,但是一頁一頁地翻看別人的 XML 尋找常見的打印錯誤並不是件令人愉快的事,而且速度太慢。我需要一種更好的辦法。

  我是“建設性偷懶”的忠誠信奉者,於是,我決定編寫一個在線工具,接受 XForms 文檔作為輸入,然後報告其中錯誤或者可能錯誤的所有標記結構。我從電子郵件文檔中合理抽取了人們所犯錯誤的樣本。二者結合起來,便成了一個強大的工具,它可以讓表單的作者自己發現錯誤。

  XForms 島

  XForms 1.0 規范(請參閱 Resources)被定義為一些元素、屬性和內容模型。但是有一點它沒有定義,就是根元素,這留給了宿主語言去解決。最常見的兩種宿主語言是 XHtml 和 SVG,但在原則上,幾乎任何 XML 詞匯表都可以使用。因此,XForms 驗證程序的第一項任務就是從文檔中分離出 XForms 部分。為此,我杜撰了 XForms 島這個詞。

  因為 XForms 將用途和表示分離開來,除了最小的表單文檔之外,所有文檔都至少有兩個 XForms 島,一個用於 XForms Model(定義表單做什麼),一個用於 XForms User Interface(定義表單的外觀)。

  清單 1 所示的是一個簡單的 XForms+XHtml 文檔——可能過於簡單了,但是也包含了一個常見的錯誤。

  清單 1. 一個普通的、帶有錯誤的 XForms+XHtml 文檔

<?XML version="1.0"?>
<html XMLns="http://www.w3.org/1999/xHtml"
   XMLns:x="http://www.w3.org/2002/xforms"
   xmlns:ev="http://www.w3.org/2001/XML-events">
 <head>
  <title>Basic mixed document</title>
  <x:model>
   <x:instance>
    <!-- @@@ forgot XMLns="" on <root> @@@ -->
    <root>
     <child/>
    </root>
   </x:instance>
   <x:bind nodeset="child" required="1"/>
  </x:model>
 </head>
 <body>
  <x:input ref="child">
   <x:label>Label</x:label>
  </x:input>
 </body>
</Html>


清單 1 顯然有兩個 XForms 島: x:model 和 x:input 。問題是代碼如何定位這兩個部分。事實上這並不是很難,因為可以標識為 XForms 島的元素必須符合兩個簡單的條件:

  必須在 XForms 名稱空間中。

  在 XForms 名稱空間中不能有任何祖先。

  雖然這種測試可以使用 XPath 完成,但我更願意探索一種不同的 Python 用處理 XML 的方法。於是我編寫了一個過濾器函數,判斷給定的節點是否啟動了一個 XML 島,清單 2 顯示了該代碼。

  清單 2. 選出 XForms 島

# extract islands
def island_filter(node, usedns):
  """
  An 'island' element has
  1) the XForms NS
  2) no ancestors with the XForms NS
  usedns is a string containing the XForms Namespace URI
  """
  if node.type != "element": return False # skip non-elements
  rc = True
  if (get_element_ns(node) != usedns): rc = False
  node = node.parent
  while node is not None:
    if (node.type=="element" and get_element_ns(node)==usedns):
      rc = False
      break
    node = node.parent
  return rc
def get_element_ns(elem):
  ns = None
  try:
    ns = elem.ns().content
  except libXML2.treeError, e:
    pass
  return ns

  XPath 檢查

  驗證程序存儲檢查出的 XForms 島,以備以後進行處理。清單 1 中的錯誤(如注釋中所述)在於不小心將 root 元素留在默認的名稱空間 XHtml 中了。這種錯誤以及其他幾種類似的問題,可以使用清單 3 中所示的 XPath 檢查探測到,清單 3 將返回可疑的元素節點。

清單 3. 用 XPath 檢查名稱空間的漏洞

//xf:instance//*[namespace-uri(.)=namespace-uri(/*) or
         namespace-uri(.)=$usedns]

  注意,因為 清單 3 沒有假定宿主語言的結構,所以大量使用了 XPath 的 // 縮寫形式,這種形式通過 descendant-or-self 軸搜索整個處理的文檔。還要注意的是,XPath 中的名稱空間前綴映射( xf: )不一定與目標文檔使用的相同( x: )。該測試檢查 x:instance 元素的後代,看它是在 XForms 名稱空間中,還是在根節點的名稱空間中。無疑名稱中的限定部分是一個很好的線索,因為完全合法的 XForms 文檔可能滿足這個條件。另一方面,這也是一個發現編輯錯誤的好機會,如 清單 1那樣,因此,驗證程序發出一條警告信息。

  連接 ID 和 IDREF

  另外一種常見的錯誤是匹配 ID 和 IDREF 。這種問題部分是由於歷史原因造成的,因為定義 ID 機制依賴於 DTD。一些工具(在很大程度上依賴於作者對 XML Schema 和 Infoset 的看法)也允許通過 XML Schema 數據類型定義 ID 。但是實際上,您很少會發現這樣的定義,遇到的常常是恰好命名為 id 的屬性。

  這種情況不怎麼好,但實用的驗證程序工具必須能處理這種情況。驗證程序查找 XForms 中包含 IDREF 的所有屬性。首先嘗試使用內置的 id() 函數;如果沒有找到匹配的元素,則轉而求助於 XPath 測試,尋找命名為 id 或者 XML:id 的屬性(根據未完成的 W3C 草案,請參閱 Resources)。代碼如下:

//*[@id='idstr' or @XML:id='idstr']

  驗證 XForms 島

  最後一步,驗證程序要分析每個 XForms 島,並使用 RELAX NG 驗證它。這項工作比看起來要復雜,因為 XForms 的每個部分(如 label )都可能包含來自宿主語言的標記,更不用說允許到處存在的其他屬性了。


 

為此,驗證程序使用了高度模塊化的 RELAX NG 模式,將其集成到非常寬容的宿主語言中。所謂“高度模塊化”是指每個元素定義、元素的屬性集、元素的內容模型都被分配了惟一的名稱,可以單獨擴展。清單 4 說明了處理單個元素定義的過程,使用的是 RELAX NG 緊湊語法。

  清單 4. RELAX NG 模塊化的元素定義

Common.Attributes = empty
Single.Node.Binding.Attributes = attribute bind { xsd:NCName } |
 (attribute model { xsd:NCName }?, attribute ref { xsd:string })
UI.Common.Attributes &=
 #host language to add Accesskey, navindex, etc. here
 attribute appearance { xsd:QName { pattern = "[^:]+:[^:]+" } |
 "minimal" | "compact" | "full" }?
Select = element select { Select.Attributes, Select.Content }
Select.Attributes &=
  Common.Attributes,
  Single.Node.Binding.Attributes,
  UI.Common.Attributes,
  attribute selection { "open" | "closed" }?,
  attribute incremental { xsd:boolean }?
Select.Content = Label, List.UI.Common.Content, UI.Common.Content

  注意,即使包含有標為 xsd:NCName 的 IDREF 屬性,也只對屬性進行詞法驗證。如前所述,實際的 ID - IDREF 連接檢查是在不同層次上進行的。分別定義不同成分的主要好處是容易擴展,比如為所有的表單控件添加 class 屬性。

  事實上,這正是宿主語言模式要做的事。驗證程序的這一部分仍在開發之中,但清單 5 說明了如何定義宿主語言。

清單 5. XForms + 宿主語言定義

Common.Attributes &=
 attribute id { xsd:NCName }?,
 attribute XML:id { xsd:NCName }?,
 attribute class { xsd:NMTOKENS }?

  如果包含在 XForms 的主模式中, 清單 5 中所示代碼就可以擴展 XForms 模式,使得日常所用結構(如 class 和 id 屬性)能夠通過驗證。因為包含的宿主語言片段基本上沒有任何限制,這一部分還需要根據具體的用法進一步調整,如上述代碼中的通配符所示。

  驗證程序工作的過程中將運行結果記錄到內存中的一個 XML 文件中。最後使用 XSL 轉換將結果轉化成 Html,並通過網絡發送出去。

  相關標准

  名稱空間一直是一個微妙的話題,對於作者而言更是如此。標准可以使事情更容易,針對這一問題,有兩種不同的正在開發之中的標准。

  一組標准稱為 Document Schema Definition Languages(文檔模式定義語言或 DSDL,請參閱 Resources),目前它們都朝著成為 ISO Final Draft International Standard 的方向發展,進展各不相同。當前這組標准被分成十個部分,DSDL 意味著默認了整個驗證問題的復雜性。其中包括 RELAX NG 的定義(第 2 部分)、Schematron(第 3 部分)和一種從大型文檔中選擇驗證部分的類似 XForms 島的機制(第 4 部分)。DSDL 的其他部分涉及到各種不同的領域,像字符指令表驗證、結合不同模式語言的方法等。

  另外一種相關標准和工具集是 OASIS 的 CAM,或者“Content Assembly Mechanism(內容組裝機制)”。這項技術可以使用業務規則定義、驗證和組合文檔,從而把模式片段組織起來,定義更大的復合文檔。

  總而言之,混合名稱空間驗證是 XML 開發中一個值得挖掘的領域。這個 XForms 驗證程序仍在開發之中,也是一個很好的學習機會。

 

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved