利用Java技术进行XML编程,第2部分(续)

 

验证XML文档

 

 

验证概述

XML 最初出现时,采用的验证方案是文档类型定义或者 DTD。DTD 来自 SGML 世界,通常只与文档的结构有关。DTD 对数据形态(data typing)的理解有限,数据形态与您在现代编程语言中看到的数据类型毫无相似之处。

比方说,我可以使用 DTD 定义 <address> 必须包含一个 <postcode> 元素,验证解析器可以保证这一点。任何没有包含 <postcode><address> 元素的 XML 文档都被标记为无效的。 不幸的是,使用 DTD 验证的解析器对于 <postcode>B9C 4F8</postcode><postcode>Mad dogs and Englishmen</postcode> 一样满意。显然,XML 世界需要更加健壮的验证语言。

为了解决这个问题,World Wide Web Consortium (W3C) 创建了 XML Schema 语言,试图满足 XML 社区的需求。XML Schema(此后我将如此称呼它)被分成两个部分:数据类型和文档结构。数据类型规范定义了一些基本数据类型和创建新类型的规则,文档结构规范定义了一组 XML 标签,规定文档中可以包含什么样的元素。

定义一种标记语言描述 XML 文档的内容是一项令人畏惧的工作,如您所料,并不是每个人都对 XML Schema 满意。本教程中还介绍另外两种模式语言: RELAX NG 和 Schematron。对这些语言的支持不如 XML Schema 那样广泛,但它们都有自己忠诚而积极地追随者。

关于 DTD 的最后一点是,它们使用了不同于 XML 文档(而且完全不兼容)的语法。这一点也传承自 SGML 世界。尽管有些人坚持认为使用不同的语法是一件好事,但多数用户更愿意用 XML 定义验证语言。 XML Schema、 RELAX NG 和 Schematron 都是基于 XML 的。无论如何,这意味着您可以编写 XSLT 样式表将模式转化成可供人类阅读的文档,从而解释模式的规则。

 

 

 

使用 DTD 定义文档

在研究各种解析器的验证之前,先看一看将要使用的文档定义。首先是 DTD:


<!ELEMENT sonnet  (author,title?,lines)>
<!ATTLIST sonnet  type (Shakespearean | Petrarchan)  
                       "Shakespearean">
<!ELEMENT author  (lastName,firstName,nationality,
                   yearOfBirth?,yearOfDeath?)>
. . .
<!ELEMENT lines (line,line,line,line,
                 line,line,line,line,
                 line,line,line,line,
                 line,line)>
<!ELEMENT line (#PCDATA)>
          

DTD 定义了所有的元素和属性。上面的语法定义了 <sonnet> 元素的 type 属性。还定义了有效值(ShakespeareanPetrarchan)和默认值((Shakespearean)。其他的语法要点有:

    • 某些元素旁边的问号表示这些元素是可选的。
    • #PCDATA 意味着元素只能包含文本,不能包含其他元素。
    • 为了规定 <lines> 元素只能包含 14 个 <line> 元素,必须列出所有 14 个元素。如果 <lines> 元素可以包含 10、 12 或 14 个 <line> 元素,那么就必须列出所有可能的情况:先是 10 个 <line> 元素,后面跟一个竖线(|);然后是 12 个 <line> 元素,后跟竖线;再后面是 14 个 <line> 元素。

 

 

使用 XML Schema 定义文档

接下来,看一看定义十四行诗文档类型的 XML Schema。模式的主要部分如下所示:


<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="sonnet">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="author"/>
        <xsd:element ref="title" minOccurs="0"/>
        <xsd:element ref="lines"/>
      </xsd:sequence>
    </xsd:complexType>
    <xsd:attribute name="type" type="sonnetType"
      default="Shakespearean"/>
  </xsd:element>
  <xsd:simpleType name="sonnetType">
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="Petrarchan"/>
      <xsd:enumeration value="Shakespearean"/>
    </xsd:restriction>
  </xsd:simpleType>
. . .
  <xsd:element name="title" type="xsd:string"/>
  <xsd:element name="lines">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="line" minOccurs="14" maxOccurs="14"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="line" type="xsd:string"/>
          

这里定义了多个项。首先是 <sonnet> 元素,它包含 <author> 元素、<title> 元素和 <lines> 元素。一些语法上的要点是:

    • minOccurs="0" 表明 <title> 元素是可选的。
    • 为属性定义一种单独的数据类型,这种数据类型以 xsd:string 为基础,可以包含两个值中的一个,ShakespeareanPetrarchan
    • 定义属性本身时也定义了属性的默认值。
    • 为了定义十四行诗包含 14 个 <line>,使用 minOccurs="14"maxOccurs="14"

 

 

使用 RELAX NG 定义文档

定义文档结构时,XML Schema 并不是惟一的选择。另外一种常见的方法是 RELAX NG,是由 XML 大师 James Clark 领导开发的一个项目。 RELAX NG 目前由 OASIS 技术委员会(请参见 参考资料)开发。引用该委员会网站上的话来说:

本技术委员会的目标是创建一种模式语言规范,这种模式语言应该简单、易于学习和使用 XML 语法。

RELAX NG 完全符合该委员会的设计目标,它的语法的确非常简单。要定义元素可以使用 <element> 元素,要说明一个元素或者属性是可选的,则使用 <optional> 元素。下面是示例文档的 RELAX NG 定义片段:


<grammar> . . . <start> <element name="sonnet"> <ref name="typeAttribute"/> <ref name="author"/> <ref name="title"/> <ref name="lines"/> </element> </start> </grammar> 

从这个简单的清单中可以看出,<sonnet> 元素似乎包含该 RELAX NG 文件中其他地方定义的 4 种成分:typeAttributeauthortitlelinestypeAttribute 是一个名为 type 的属性,可以包含两个值中的一个,ShakespeareanPetrarchan。其定义如下:


<define name="typeAttribute"> <attribute name="type"> <choice> <value>Shakespearean</value> <value>Petrarchan</value> </choice> </attribute> </define> 

RELAX NG <define> 元素类似于一个置换函数,在任何引用定义的地方(如 <ref name="typeAttribute"> 中),都将引用替换为 <define> 元素的内容。

提示: RELAX NG 定义了一种规定默认属性值的方法,详情请参阅 参考资料

与 XML Schema 相比,RELAX NG 欠缺的一点是没有定义基数的机制,即 XML 文档中特定部分元素可以出现的个数。在 XML Schema 中,您使用 minOccurs="14"maxOccurs="14" 定义了一个包含 14 个 <line> 元素的 <lines> 元素。而在 RELAX NG 中,您只能将这些元素一一列举出来:


<define name="lines"> <element name="lines"> <ref name="line"/> <ref name="line"/> <ref name="line"/> . . . <ref name="line"/> </element> </define> 

 

 

使用 Schematron 定义文档

Schematron 文档使用 XPath 表达式定义有效 XML 文档的内容。在下面的示例中,Schematron <assert> 元素被用来定义十四行诗的规则。每个 <assert> 语句都有一个 test 属性,如果 test 成立,则将 <assert> 元素中的文本作为错误消息输出。

下面的示例是 <lines> 元素的 Schematron 规则:


<rule context="lines"> <assert test="count(line) = 14"> A sonnet must have 14 <line>s. </assert> <assert test="count(line) = count(*)"> The <lines> element can only contain <line> elements. </assert> </rule> 

第一个约束是 <lines> 元素必须包含 14 个 <line> 元素。第二个约束稍微复杂一点,它规定 <lines> 元素不能包含其他元素。定义的方式是规定 <line> 元素的个数必须等于所有元素的个数。

 

 

 

文档验证

现在已经介绍了 4 种定义有效 XML 文档内容的方法,下面想说明如何使用这些定义。因为并非所有的工具都支持这 4 种验证方法,我将结合使用下列工具和技术:

    • DTD 和 XML Schema: DOM 和 SAX 解析器都被用来验证具有 DTD 和模式的文档。
    • RELAX NG: James Clark 的开放源代码工具 Jing 针对 RELAX NG 模式验证 XML 文档。
    • Schematron: Schematron 使用 XSLT 样式表引擎生成新的样式表,然后再一次使用 XSLT 引擎验证 XML 文档。

 

 

使用 SAX 验证

为了使用 SAX 验证 XML 文档,您需要对 SAXParserFactory 和它创建的 SAXParser 定义两个性质。这里和前面的例子不同,在这里必须设置工厂对象的性质然后创建解析器对象。代码如下:


SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); spf.setValidating(true); SAXParser sp = spf.newSAXParser(); sp.setProperty ("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); sp.parse(uri, this); 

前两个性质为 SAXParserFactory 创建的所有的 SAXParser 打开名称空间和验证特性。schemaLanguage 性质定义了使用何种模式语言验证文档。这里使用的值(http://www.w3.org/2001/XMLSchema)是 JAXP 定义的。给定的解析器也可能定义其他的值来表明支持 RELAX NG、 Schematron 或者其他某种语言,尽管到 2004 年 6 月为止还没有哪一种流行的解析器这样做。

除了 JAXP 性质外,某些解析器还在 JAXP 标准化它们之前定义了自己的性质名和值。虽然您也可以使用这些性质,但不建议这样做。使用这里所用的 JAXP 性质意味着您的代码不会被捆绑到特定的解析器上。

现在您已经对工厂和解析器对象设置了这三个性质,可以准备验证文档了。编辑 sonnetSchema.xml 并在 <author> 元素中添加一个 <nickname> 元素:


. . . <author> <lastName>Shakespeare</lastName> <firstName>William</firstName> <nickname>Shakin' Billy</nickname> <nationality>British</nationality> . . . 

当使用验证程序检查该文档时,结果如下:


C:/adv-xml-prog>java SaxValidator sonnetSchema.xml [Error] sonnetschema.xml:9:15: cvc-complex-type.2.4.a: Invalid content was found starting with element 'nickname'. One of '{" ":nationality}' is expected. Your document is not valid. 

 

 

 

使用 DOM 解析器验证

使用 DOM 解析器验证 XML 文档的过程和使用 SAX 解析器类似。我将说明如何创建 DocumentBuilderFactory,设置它的某些性质,然后创建一个解析器(DocumentBuilder)。在解析和验证 XML 文档之前,需要为 DOM 解析器定义一个错误处理程序。奇怪的是,这个错误处理程序是一个 SAX 错误处理程序,这反映了 DOM 解析器通常建立在 SAX 解析器基础上这一事实。

下面是设置解析器工厂和创建解析器的代码部分:


DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); dbf.setValidating(true); 

现在您已经建立了解析器工厂,在创建解析器之前还有一件事要做,即定义解析器使用的模式语言。如果 XML 文档使用的是 DTD,那么就不需要对解析器工厂设置任何特殊的性质。另一方面,如果文档使用 XML Schema,则必须定义 schemaLanguage 性质:


if (useSchema) dbf.setAttribute ("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); 

使用命令行参数定是否要使用 XML Schema 或 DTD。

现在可以创建解析器对象了,一旦创建了解析器,还需要设置错误处理程序:


DocumentBuilder db = dbf.newDocumentBuilder(); db.setErrorHandler(this); doc = db.parse(uri); 

为了简单起见,就在 DomValidator 代码中实现错误处理程序接口。(从技术上讲,实现的是 SAX DefaultHandler 接口,其中包括 ErrorHandler。)这意味着可以根据需要实现 warning()error()fatalError() 方法。

 

 

 

使用 Jing 验证

使用 Jing 验证文档相对要简单一些。因为 Jing 本身是一种验证工具,不需要编写任何代码就可以针对 RELAX NG 验证 XML 文档。其命令行语法如下:


c:>java -jar c:/jing-20030619/bin/jing.jar sonnet.rng sonnet.xml 

这个可执行 jar 文件的第一个参数是 RELAX NG 模式,第二个文件是要验证的 XML 文档。如果文档是有效的,则不显示任何消息;否则将有消息告诉您错误出现的位置。比如,如果删除了 <firstName> 元素,就会得到下面这样的消息:


c:>java -jar c:/jing-20030619/bin/jing.jar sonnet.rng sonnet.xml sonnet.xml:6:18: error: required elements missing c:> 

虽然这些消息不是非常详尽,但它确实给出了文档出错的行号和列号。

如果希望把 Jing 嵌入到代码中,最好的办法是使用 com.thaiopensource.relaxng.util.Driver 类中的代码(当然,之后是随 Jing 发布的 Jing Copying Conditions)。

 

 

 

使用 Schematron 验证

Schematron 采用独特的方法定义文档内容:使用 XSLT 样式表。使用 Schematron 验证 XML 文档需要三个步骤:

    • 创建符合 Schematron 规则的 XML 文档(sonnetSchematron.xml)。
    • 使用 Schematron 样式表将文档规则转化成新的样式表(sonnetRules.xsl),用于验证您的文档类型。
    • 使用定制样式表转换 XML 文档。如果文档有效,则转换不会生成任何错误消息;否则样式表将生成一个错误报告。

可以用图形来表示这三个步骤: Schematron 验证过程

您已经完成了第一步,下一步是将 Schematron 文当规则转化成定制的样式表。为此需要有 schematron-basic.xslskeleton1-5.xsl 文件,可以从 Schematron 项目的主页(参见 参考资料)上找到该文件。假设您使用的是 Xalan XSLT 引擎,可以使用下面的命令行生成定制样式表:


java org.apache.xalan.xslt.Process -in sonnetSchematron.xml -xsl schematron-basic.xsl -out sonnetRules.xsl 

这样就生成了文件 sonnetRules.xsl,一个用来检查十四行诗文档有效性的样式表。使用 sonnetRules.xsl 需要再次运行样式表引擎:


java org.apache.xalan.xslt.Process -in sonnet.xml -xsl sonnetRules.xsl 

如果 sonnet.xml 文档有效,则不会产生任何错误消息。如果出现错误,就会看到前面编写的 <assert> 元素的输出。比方说,如果 14 个 <line> 元素中的某个元素出现在十四行诗之外,就会看到下面的错误消息:


In pattern count(line) = 14: A sonnet must have 14 <line>s. 

Schematron 的一个长处是可以定义自己的错误消息。如果不喜欢上面的消息,您可以修改它。Schematron 是一种有趣的验证方法。目前它正逐渐被 ISO 接受为标准,要想获得更多信息,请访问 Schematron 项目的主页,您也可以从那里下载该示例中用到的样式表。

 

 

 

 

 

结束语和参考资料

 

 

结束语

本教程讨论了各种不同的技术和 API。所有这些最终都归结到验证上,使用验证需要设置解析器的特性,验证 XML 文档解析器需要具有名称空间感知特性。以前的教程已经讨论了一些不同的标准、API 和方法,您可以选择最适合您的工具。本系列的最后一篇教程中,我将考察如何从头创建 DOM 和 SAX 结构,将数据结构从一种 API 转化到另一种 API,以及 DOM 和 SAX API 的一些高级特性。

 

 

 

参考资料

下载本教程中完整的例子, 。

回顾本系列中上一篇教程,“利用 Java 技术进行 XML 编程,第 1 部分”(developerWorks,2004 年 1 月)。在本教程中, Doug Tidwell 讨论了使用 Java 技术处理 XML 文档的基础知识,并考察了常见的 XML API。如果不熟悉 XML,请阅读 Doug 撰写的“XML 入门”教程(developerWorks,2002 年 8 月)。

请访问 W3C 网站上的 DOM 技术报告页面,上面列出了与 DOM 有关方面的各种链接。要想查看各个规范,请参阅:

阅读 SAX Version 2.0

通过 JDOM 项目主页 进一步了解 JDOM。

阅读 Namespaces in XML

参阅 W3C XML Schema 导引W3C XML Schema 结构规范W3C XML Schema 数据类型规范。这些都是很好的参考资料,但要想获得最常用的一些模式定义的信息,您可以在导引中更快地找到您想要的。

请访问 RELAX NG 项目主页学习 RELAX NG。还可以访问 OASIS 上的 RELAX NG 项目页面。 Doug 曾经提到 RELAX NG 有一个 DTD Compatibility 文档,它定义了“数据类型和符号支持 RELAX NG 中没有直接支持的 XML 1.0 DTD 的一些特性。”

请访问 Schematron 项目主页

关于有效统一资源标识符的所有细节,请参阅 URI 标准(RFC2396)

关于结合 RELAX NG 和 Schematron 尝试,请阅读 Eddie Robertsson 为 XML.com 撰写的精彩文章

developerWorks Developer Bookstore 提供了大量精彩的书籍,其中包括这些关于 XML 和 Java 技术的书籍

developerWorks XMLJava 技术专区可以找到更多与本文主题有关的更多资源。

了解如何才能成为一名 IBM 认证的 XML 及相关技术的开发人员

 

 

 

反馈

请将关于本教程的反馈发送给我们。我们期望听到您的意见!此外,您也可以通过 dtidwell@us.ibm.com 与作者 Doug Tidwell 联系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值