IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  XML  >

XForms 验证程序揭密

混合名称空间验证的挑战

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

Micah Dubinko (micah@brainattic.info), 首席顾问, Brain Attic, L.L.C.

2004 年 9 月 01 日

对混合名称空间文档进行验证更像是一门艺术,而不像是一门科学。XForms 1.0 在各种各样的宿主语言中是作为一种组件使用的,它引入了一些关于验证程序应该如何处理这类文档的新问题。本文将讨论作者在编写在线 XForms 验证程序工具时所遇到的挑战,以及克服这些问题的技术。

我撰写的 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:modelx: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

另外一种常见的错误是匹配 IDIDREF 。这种问题部分是由于历史原因造成的,因为定义 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:NCNameIDREF 属性,也只对属性进行词法验证。如前所述,实际的 ID - IDREF 连接检查是在不同层次上进行的。分别定义不同成分的主要好处是容易扩展,比如为所有的表单控件添加 class 属性。

事实上,这正是宿主语言模式要做的事。验证程序的这一部分仍在开发之中,但清单 5 说明了如何定义宿主语言。


清单 5. XForms + 宿主语言定义
Common.Attributes &=
  attribute id { xsd:NCName }?,
  attribute xml:id { xsd:NCName }?,
  attribute class { xsd:NMTOKENS }?

如果包含在 XForms 的主模式中, 清单 5 中所示代码就可以扩展 XForms 模式,使得日常所用结构(如 classid 属性)能够通过验证。因为包含的宿主语言片段基本上没有任何限制,这一部分还需要根据具体的用法进一步调整,如上述代码中的通配符所示。

验证程序工作的过程中将运行结果记录到内存中的一个 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 验证程序仍在开发之中,也是一个很好的学习机会。



参考资料



关于作者

Micah Dubinko 的照片

Micah Dubinko 是 Brain Attic, L.L.C. 的顾问和缔造者,这是一家专门消除信息过载的软件生产和咨询公司。他为 O'Reilly Media 撰写了 XForms Essentials 一书,并参加了开发 XForms 1.0 的工作组。他在 Phoenix, AZ 定居并工作。您可以通过 micah@brainattic.info与他联系。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?




回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款