用 Schematron 改进 XML 文档验证

Dare Obasanjo
Microsoft Corporation

适用于:
XML
Schematron

**摘要:**Dare Obasanjo 描述了如何使用超出 W3C XML 架构能力的 Schematron XML 验证语言在 XML 文档上强制约束。

单击此处可下载本文的代码示例。

本页内容

简介
出色的6个元素:Schematron 的基本元素
将 Schematron 与 W3C XML 架构验证相结合
在 .NET Framework 中使用 Schematron
小结

简介

XML 架构可用于描述一个 XML 文档的结构,方法是:指定能够在文档中发生的有效元素、元素发生的顺序以及这些元素某些方面上的表达约束。由于 XML 和 XML 架构语言的使用范围越来越广泛,已经围绕 XML 文档验证和 XML 架构开发了两个主要使用情况。

  1. **描述并强制 XML 文档的生产者和使用者之间的协定:**XML 架构是一个相当简洁的机器可读方法,用于描述根据特定的 XML 词汇,一个有效的 XML 文档是由什么构成的。因而,架构可以被视为 XML 文档的生产者和使用者之间的“协定”。通常,使用者通过根据架构验证接收到的文档,来确保从生产者接收的 XML 文档符合协定。上述说明涵盖了大量的 XML 使用情况,从交换 XML 文档的业务实体到使用 XML 配置文件的应用程序,以及介于两者之间的许多情况。

  2. **为处理和存储表示为 XML 文档的类型化数据创建基础:**由于 XML 成为一种表示严格结构化的强类型数据(如关系数据库的内容或编程语言对象)的流行方法,因此能够描述 XML 文档内的数据类型的需求就变得很重要。这就导致了 XML 架构语言的创建,XML 架构语言提供了将输入 XML 信息集转换为类型批注信息集 (TAI)(其中,用一个类型名称来批注元素和属性信息项)的机制。W3C XML 架构建议将类型批注信息集的创建描述为根据架构进行文档验证的结果。在根据 W3C XML 架构进行验证期间,会将一个输入 XML 信息集转换为架构后验证信息集(post schema validation infoset,PSVI),除了其他内容外,PSVI 还包含类型批注。不过,实践经验表明,并不需要执行完整文档验证来创建类型批注信息集,通常,许多使用 XML 架构创建强类型 XML(如 XML<->object 映射技术)的应用程序都不执行完整的文档验证。

目前,最流行的 XML 架构语言是 W3C XML 架构定义语言 (XSD)。尽管 XSD 能够满足涉及类型批注信息集的情况,但对于描述 XML 文档结构的约束,它是相当有限的。对 使用 W3C XML 架构所提供的约束无法表达 XML 词汇设计中的通用成语的情况有很多。W3C XML 架构无法描述的三个最常请求的约束是:

  1. 指定属性选择的能力。例如,server-status 元素应该有一个 server-uptime 属性或一个 server-downtime 属性。

  2. 将元素和属性分组到模型组中的能力。尽管我们可以使用诸如 xs:sequencexs:choicexs:all 之类的编写器来分组元素,但不能同时对元素和属性两者做相同的处理。例如,不能在一组元素和属性与另一组元素和属性之间创建选择。

  3. 基于一个元素或属性的值改变内容模型的能力。例如,如果 status 属性的值为 available,那么该元素应该有一个 uptime 子元素,否则就应该有一个 downtime 子元素。此类约束的技术名称是共现 (co-occurrence) 约束。

尽管这些习惯用语广泛地用在 XML 词汇中,但是却无法使用 W3C XML 架构来描述它们,这使得依赖架构验证来强制消息协定变得很困难。本文描述如何使用 Schematron 将此类功能分层到 W3C XML 架构语言的顶部。

出色的6个元素:Schematron 的基本元素

Schematron 断言语言提供了一种机制来对使用 XPath 表达式的 XML 文档的有效性进行断言。在一个 Schematron 文档中有 6 个常用元素:schemanspatternruleassertreport。Schematron 断言语言所使用的元素的命名空间 URI 是 **http://www.ascc.net/xml/schematron**。

  • <schema>元素:架构的文档元素。其子元素包括 title 元素(包含架构的可读名称)、ns 元素(用于指定架构中的 XPath 表达式所使用的 namespace<->prefix 绑定)、phase 元素(描述应该一起执行的模式组)、pattern 元素(包含要根据其验证文档的规则组)以及 diagnostics 元素(用于在文档断言失败时提供较细粒度的错误消息)。schema 元素还包含以下属性:idversionschemaVersionfpidefaultPhaseicon。下面是 schema 元素的一个示例:

    <schema xmlns="http://www.ascc.net/xml/schematron" 
        schemaVersion="1.01" >
    

<title>A Schema for Books</title> <ns prefix="bk" uri="http://www.example.com/books" /> <pattern id="authorTests"> <rule context="bk:book"> <assert test="count(bk:author)!= 0"> A book must have at least one author </assert> </rule> </pattern> <pattern id="onLoanTests"> <rule context="bk:book"> <report test="@on-loan and not(@return-date)"> Every book that is on loan must have a return date </report> </rule> </pattern> </schema>

  • <ns>元素:它用于指定 XPath 表达式在 patternruleassert 元素中使用的 prefix<->namespace 绑定。它有一个必需的 prefixuri 属性,它们用于定义命名空间前缀和与该前缀绑定的命名空间名称。下面是 ns 元素的一个示例。

    <ns prefix="bk" uri="http://www.example.com/books" />
    
  • <pattern>元素pattern 包含一个 rule 元素列表。pattern 元素还包含以下属性:idnameseeicon。模式的主要用途是将类似的断言分组到一起,这样我们就可以创建阶段 — 在其中为验证管道的不同阶段执行不同模式的组合。下面是 pattern 元素的一个示例:

    <pattern id="authorTests"
    

see="http://www.example.com/books/guidelines.html" name="Test for non-zero number of authors"> <rule context="bk:book"> <assert test="count(bk:author)!= 0"> A book must have at least one author </assert> </rule> </pattern>

  • <rule>元素assertreport 元素包含在 rule 元素中。rule 元素有一个包含 XPath 表达式的 context 属性。来自匹配 context 属性所指定的 XPath 表达式的输入文档的所有节点,随后会根据 rule 中的每个 assertreport 元素来测试,以检查它们是否满足断言。规则还有一个用于提供宏包含机制的 abstract 属性。当属性值为 true 时,该规则的内容可以包含在其他 rule 元素中。rule 元素可以具有一个或多个引用抽象 ruleextends 元素。在验证期间,ruleextends 元素由目标抽象 rule 的内容所替换。rule 还可以包含一个 key 元素,该元素提供一个在文档的部分之间定义交叉引用的机制,它类似于 XSLT 中的 key 元素key 元素具有一个 name 属性,用于标识 key 和包含要与其匹配的 XPath 表达式的 path 属性。下面是 rule 元素的一个示例:

    <rule context="bk:book" role="authorCountRule">
      <assert test="count(bk:author)!= 0">
       A book must have at least one author
      </assert>
    </rule>
    
  • <assert>元素assert 元素提供的机制用于测试关于元素内容模型的语句(即断言)是否为真。这个元素的 test 属性包含一个 XPath 表达式。如果使用 XPath boolean() 函数将 XPath 查询结果转换为布尔值之后结果为 false,则会出现一个验证错误。在这种情况下,assert 元素的内容将作为错误消息发出。对于错误消息,assert 元素允许混合内容(文本与元素交错)。大多数允许作为子元素的元素都是用于可视化布局的,而且是从 HTML 借用的,如 pemphdir。最后允许的子元素是 name 元素。当错误消息发出时,name 元素将被 context 元素的名称所替换。为了使架构作者能够重复使用错误消息,assert 元素可以包含一个 diagnostics 属性,这个属性可以引用架构的 diagnostics 元素中的一个或多个 diagnostic 元素。这样,如果出现验证错误,assert 元素的内容和所引用的 diagnostic 元素的内容都会被发出。assert 元素还具有以下其他属性:idrolesubjecticon。下面是 assert 元素的一个示例:

    <assert test="count(bk:author)!= 0">
       A book must have at least one author
    

</assert>

  • <report>元素:除了一个至关重要的差异外,这个 report 元素与 assert 元素完全相同。如果使用 XPath boolean() 函数将 test 属性中的 XPath 查询结果转换为布尔值之后结果为 true,则会出现一个验证错误。下面是 report 元素的一个示例:

    <report test="@on-loan and not(@return-date)">
    

Every book that is on loan must have a return date </report>

有关 Schematron 中的元素的额外信息,请从 ZVON Schematron reference 中获取

将 Schematron 与 W3C XML 架构验证相结合

使用 Schematron 进行验证的一个问题是,它使得指定 XML 文档的结构变得很麻烦。相反,这项任务在 W3C XML 架构中相当简单。那么,您可以两者都用并双收其利,因为您可以将 Schematron 规则嵌入到 W3C XML 架构中

W3C XML 架构建议允许应用程序通过在特定架构元素的 xs:annotation 内的 xs:appinfo 元素中添加应用程序特定的数据,来扩展架构验证。您可以将 Schematron pattern 元素嵌入这些扩展块中,它们随后可以用作架构验证过程的一部分。模式使用的命名空间应该用 ns 元素在架构顶层的 xs:annotation 中声明。下面的示例显示了一个 W3C XML 架构,它利用嵌入的 Schematron 规则来定义超出 W3C XML 架构能力的约束。具体来说,此模式强制为,如果可选的 on-loan 属性出现在 book 元素上,则还必须有一个 return-date 属性。

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"  
    xmlns:sch="http://www.ascc.net/xml/schematron"
    targetNamespace="http://www.example.com/books"
    xmlns:bk="http://www.example.com/books"
    elementFormDefault="qualified" >

  <xs:annotation>
    <xs:appinfo>
     <sch:title>Schematron validation</sch:title>
     <sch:ns prefix="bk" uri="http://www.example.com/books"/>
    </xs:appinfo>
   </xs:annotation>

 <xs:element name="books"> 
  <xs:complexType>
   <xs:sequence>  
    <xs:element name="book" type="bk:bookType" maxOccurs="unbounded">
      <xs:annotation>
       <xs:appinfo>
        <sch:pattern id="onLoanTests">
          <sch:rule context="bk:book">
           <sch:report test="@on-loan and not(@return-date)">
           Every book that is on loan must have a return date
           </sch:report>
          </sch:rule>
        </sch:pattern>
       </xs:appinfo>
      </xs:annotation>
    </xs:element>
   </xs:sequence> 
  </xs:complexType>
 </xs:element>

 <xs:complexType name="bookType">
  <xs:sequence>
   <xs:element name="title" type="xs:string" />
   <xs:element name="author" type="xs:string" />
   <xs:element name="publication-date" type="xs:date" />
  </xs:sequence>
  <xs:attribute name="publisher" type="xs:string" use="required" />
  <xs:attribute name="on-loan" type="xs:string"  use="required" />
  <xs:attribute name="return-date" type="xs:date"  use="required" />
 </xs:complexType>

</xs:schema> 

在 .NET Framework 中使用 Schematron

这个有关使用嵌入在 XML 架构中的 Schematron 规则而获得的附加灵活性的讨论,如果无法实际在 .NET Framework 中实现的话,将没有任何意义。有一个由 Microsoft XML MVP Daniel Cazzulino 实现的针对 .NET Framework 的 Schematron 实现(称为 Schematron.NET),它提供用于根据 Schematron 架构和包含嵌入的 Schematron 规则的 XML 架构来验证 XML 文档的类。大多数用户将在 Schematron.NET 中与之直接交互的类是 NMatrix.Schematron.Validator 类。下面是对这个类的 API 的概述。

构造函数

  1. 一个指示作为调用 Validate() 的结果而返回的 IXPathNavigable 实例应该是 XmlDocument 还是 XPathDocument 的重载。

    public Validator(NavigableType type) 
    
  1. 一个指示输出格式的重载。

    public Validator(OutputFormatting format) 
    
  1. 一个指示输出格式以及作为调用 Validate() 的结果而返回的 IXPathNavigable 实例应该是 XmlDocument 还是 XPathDocument 的重载。

    public Validator(OutputFormatting format, NavigableType type) 
    
  1. XPathDocument 设置为由 Validate() 方法返回的 IXPathNavigable 类型并将 OutputFormatting.Log 用作格式化程序的默认构造函数。

    public Validator()
    

属性

  1. 计算上下文。

    public EvaluationContextBase Context { get; set; } 
    
  1. Validate() 方法返回的类型是 XmlDocumentXPathDocument

    public NavigableType ReturnType { get; set; } 
    
  1. 用于从验证生成输出的格式化程序。

    public IFormatter Formatter {get; set;}
    
  1. 其模式应该在验证期间计算的 phase 元素的一个标识符。

    public string Phase {get; set;}
    

方法

  1. 将一个 XML 架构或 Schematron 规则文件添加到验证程序的架构集。

    public void AddSchema(XmlSchema schema)
    

public void AddSchema(Schema schema) public void AddSchema(Stream input) public void AddSchema(TextReader reader) public void AddSchema(XmlReader reader) public void AddSchema(string uri schema)

  1. 将 XML 架构集合或 Schematron 规则文件添加到验证程序的架构集。

    public void AddSchemas(XmlSchemaCollection schemas)
    

public void AddSchemas(SchemaCollection schemas)

  1. 根据所提供的 Schematron 规则验证输入文档。如果 Schematron 规则嵌入在 XML 架构中,则 XSD 验证不会执行。

    public void ValidateSchematron(IXPathNavigable source)
    

public void ValidateSchematron(XPathNavigator nav)

  1. 根据所提供的架构验证输入文档。如果所提供的架构是一个带有嵌入 Schematron 规则的 XML 架构,那么 Schematron 和 XSD 验证都会执行。

    public IXPathNavigable Validate(string uri)
    

public IXPathNavigable Validate(Stream input) public IXPathNavigable Validate(XmlReader reader) public IXPathNavigable Validate(TextReader reader)

示例

下面的代码示例根据本文的将 Schematron 与 W3C XML 架构验证相结合部分中的 XML 架构,来验证 books.xml 文件。

using System;
using System.Xml;
using NMatrix.Schematron;

class Program{

  public static void Main(string[] args){

    try{
     Validator validator = new Validator();
     validator.AddSchema("books.xsd"); 
     validator.Validate(new XmlTextReader("books.xml"));
 
    }catch(Exception e){
      Console.WriteLine(e);
    }
  }
}

小结

本文展示了同时使用 Schematron 和 W3C XML 架构来双收其利是可能的。我们可以使用 XSD 创建类型化 XML 文档,但仍然可以用声明性方式从架构语言获得业务规则的丰富验证。通过从 SourceForge 或本文随附的下载资料中的程序集下载 Schematron.NET,使用 .NET Framework 的开发人员现在就可以开始使用 Schematron。

对于您的 XML 验证需要,现在就开始使用 Schematron 吧!它真正提供了两者最好的东西。

转到原英文页面