使用 XSLT 和 Java 扩展验证 XML 文档中的复杂约束

原创 2006年06月07日 13:35:00

Peter Heneback (peter.heneback@uk.ibm.com), 顾问, IBM

2006 年 6 月 05 日

 

基于语法的验证语言,例如 XML Schema 和 DTD,可以很好地确保 XML 文档遵从定义良好的消息结构。这样可确保接收 XML 消息的应用程序能够正确地处理接收到的 XML 消息,但是不能保证包含在消息中的数据是有效的。例如,基于语法的验证语言的这些局限性意味着必须使用不同的方法来验证变量和外部数据集上的同现约束(co-occurrence constraints)和其他约束。

在很多情况下,用 XML Schema 或 DTD 不能实现的验证逻辑被并入到应用程序代码中。这种解决方案比较容易实现,但是得到的实现常常不够灵活。本文首先调查研究 Schematron 这种解决上述问题的方法,接着指出这种方法的一些缺点。然后,本文探索一种使用 W3C 标准组件并结合 Java 扩展和开放源代码 XSLT 处理器的方法。

 

常被推荐的用于补充 XML Schema 的方案是使用 Schematron。Schematron 是基于规则的语言,使用 XPath 来表达关于 XML 实例文档中的内容的断言。这是通过用一个基本样式表转换 Schematron 模式,将该模式转换成一个 XSLT 样式表来完成的。转换的结果是一个 XML 格式的报告,其中包含关于哪些断言失败的详细信息,并附有注释。然而,Schematron 不大适用于定义结构,因为用 Schematron 定义结构很快就会变得吃力起来。因此,仍然需要首先用 XML Schema 对文档进行验证,但是 XML Schema 和 Schematron 一起可以满足大多数应用程序的验证需求。实际上,在 XML Schema 语法中,由于 Schematron 断言处在不同的名称空间里,两者通常可以包括在同一个文件中,并且分别作为文档验证过程的一部分。

图 1 描绘了一个很常见的应用场景的逻辑处理步骤。XSLT 首先验证传入的 XML 实例文档,然后在应用程序本身对它进行处理或者它被发送到外部应用程序之前对它进行转换。从图中很容易看出,当结合使用 XML Schema 和 Schematron 第一次执行验证,然后使用一个 XSLT 样式表转换验证过的文档时,这一操作变得复杂起来。可以通过将模式分离,提前转换 Schematron 模式来缩短该过程,但是这仍然需要运行两次 XSLT 处理器,并且需要解析和检查由 Schematron 转换产生的验证报告。



使用 Schematron 验证 XML 数据

 

  • 在使用 Schematron 模式验证 XML 文档之前,至少需要使用基本样式表对该模式进行一次转换。如果 Schematron 语法包括在 XML Schema 中,那么还需要进一步的转换。
  • Schematron 目前不能直接将报告返回给应用程序。在进行验证之后,需要手动地或自动地对报告加以处理。

 

在本节中,我将介绍 XSLT 加 Java 扩展这种补充基于语法的 XML Schema 验证的方法,这是一种基于规则的方法。我将带您亲历一组简单的示例。至于如何使用 XML Schema 实现 XML 验证器(validator),本文不作介绍,因为在 developerWorks 上的很多文章和教程上对此作了大量的阐述(参见 参考资料)。

图 2 展示了与 图 1 相同的场景,不过这次使用 XSLT 加 Java 扩展在一步内执行验证和转换。这一次不再产生一个报告,然后又必须单独处理该报告,而是直接将验证失败以一个 ValidationException 对象的形式返回给应用程序,其中 ValidationException 类扩展了 Exception 类。除了减少文档经过 XSLT 处理器的次数以外,在第一次验证失败时就会停止转换,从而防止对无效数据的不必要的处理。



使用 XSLT 和 Java 扩展进行验证

 

为了演示如何使用 XSLT 和 Java 进行验证,我使用一个简单的登记表的转换作为示例输入,该登记表包含雇员详细信息,例如姓名、电话号码、称呼和性别。稍后可以看到,称呼和性别信息在同现约束的中央。清单 1 包含 XML 输入文档的一部分。



<input:staff xmlns:input="cross-field-validation-namespace">

...

  <input:employee id="1234A">
    <input:first_name>Julia</input:first_name>
    <input:last_name>Smith</input:last_name>
    <input:title>Mrs</input:title>
    <input:gender>F</input:gender>
    <input:telephones>
      <input:mobile preferred="false">0770-555 1231</input:mobile>
      <input:mobile preferred="true">0771-555 1232</input:mobile>
      <input:home preferred="false">0207-555 1233</input:home>
    </input:telephones>
  </input:employee>

...

</input:staff>

这个例子中的转换简单地将雇员登记表中的姓和名合并在一起。该转换还提取那些 preferred 属性被设置为 true 的电话号码,如 清单 2 所示。



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
  xmlns:input="cross-field-validation-namespace">

  <xsl:template match="/input:staff">
    <phones>
        <xsl:apply-templates select="input:employee" />
    </phones>
  </xsl:template>

  <xsl:template match="input:employee">
    <employee>
      <name>
        <xsl:value-of select="concat(input:first_name,' ',input:last_name)" />
      </name>
      <tel>
        <xsl:value-of select="input:telephones/*[@preferred = 'true']" />
      </tel>
    </employee>
  </xsl:template>

</xsl:stylesheet>

清单 3 显示了通过用 清单 2 中的 XSLT 转换输入文档而得到的输出文档。



<phones xmlns:input="cross-field-validation-namespace"
  xmlns:exception="xfield.exception.ValidationExceptionThrower"
  xmlns:xalan="http://xml.apache.org/xslt">
  <employee>
    <name>Julia Smith</name>
    <tel>0771-555 1232</tel>
  </employee>
  <employee>
    <name>John Smith</name>
    <tel>0207-555 1236</tel>
  </employee>
  <employee>
    <name>Jenny Smith</name>
    <tel>0770-555 1237</tel>
  </employee>
</phones>

 

为了通过 XSLT 处理器将验证错误返回给应用程序,只需要两个非常简单的 Java 类。为此,必须创建 ValidationException 类和 ValidationExceptionThrower 类。ValidationException 类是标准 Java Exception 类的一个简单的扩展,它使应用程序可以将验证错误与处理器抛出的其他异常区分开来。当 ValidationExceptionThrower 类的 throwException 方法被调用时,该类简单地抛出 ValidationException。为此另外还需要一个类。当在 XSLT 中使用 Java 扩展时,不能使用常规的 Java throw 语法抛出异常。只能使用用于 XSLT 的 Java 扩展来创建对象和调用方法。清单 45 展示了 Java 扩展所需的两个类的完整源代码。



package xfield.exception;

public class ValidationException extends Exception{

  public ValidationException(String sMsg)
  {
    super(sMsg);
  } // end constructor

} // end class




package xfield.exception;

public class ValidationExceptionThrower {

  public ValidationExceptionThrower()
  {
    // Emtpy
  } // end constructor

  public void throwException(String sMessage) throws Exception
  {
    throw new ValidationException(sMessage);
  } // end throwException

} // end class

 

如前所述,本文使用称呼和性别作为一个常见同现约束的例子。您需要检查称呼和性别元素是否匹配。例如,如果 title 被设为 Mr,那么 gender 应该被设置为表示男性的 M。但是,要验证 title 和 gender 元素是否分别包含有效的值,最好的做法是使用 XML Schema 中的枚举。

为了进行验证,将转换代码放入到 <choose/> 子句的 <otherwise/> 块中,并检查 <when/> 块中的 test 语句。如果 test 语句的值等于 false,也就是说验证成功,那么就会执行转换代码。如果 test 语句的值等于 true,那么就会使用一个 Java 扩展在 ValidationExceptionThrower 类的一个实例上调用 throwException()。注意,在 XSLT 的根标记中附加了一个带前缀 exception 的名称空间,其中包含类名和包名。然后这个前缀被像一个对象名那样用来调用 throwException() 方法,调用时以一个错误字符串作为参数。回到 ValidationExceptionThrower 类的 Java 代码上来,很容易看出一个包含 XSLT 中的错误字符串的 ValidationException 是如何被抛出的。

为了检查进一步的状况,添加所需的 <when/> 语句,并添加适当的错误消息作为这些方法的参数。



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
  xmlns:exception="xfield.exception.ValidationExceptionThrower"
  xmlns:input="cross-field-validation-namespace">

...

<xsl:template match="input:employee">
  <xsl:choose>
    <xsl:when test="input:gender = 'M' and input:title != 'Mr' 
                 or input:gender = 'F' and input:title = 'Mr'">
      <xsl:value-of
        select="exception:throwException('Gender and title do not match')"/>
    </xsl:when>
    <xsl:otherwise>
       <employee>
         <name>
           <xsl:value-of select="concat(input:first_name,' ',input:last_name)"/>
         </name>
         <tel>
           <xsl:value-of select="input:telephones/*[@preferred = 'true']"/>
         </tel>
       </employee>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

清单 7 所示,添加 XPath 表达式到异常消息将产生更详细的对验证失败的描述,这样大大方便了发现问题。



<xsl:when test="input:gender = 'M' and input:title != 'Mr' 
          or input:gender = 'F' and input:title = 'Mr'">
  <xsl:value-of select="exception:throwException(
                  concat('Gender and title do not match for employee ',
                          input:first_name,' ',input:last_name))" />
</xsl:when>

 

如果需要将转换和验证逻辑分离开来,那么可以使用一个标准的 <include/> 指令来引用验证检查,并将它们与转换代码一起执行。清单 8 展示了转换代码,其中 <include/> 标记引用了文件 validate.xsl。还应注意添加的对名为 validate 的模板的调用。这个调用应该与被包括的文件中包含条件检查的模板的名称相匹配,如 清单 9 所示,这一点很重要。



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
  xmlns:input="cross-field-validation-namespace">

  <xsl:include href="validate.xsl" />

  <xsl:template match="/">
    <xsl:call-template name="validate" />
    <phones>
      <xsl:apply-templates />
    </phones>
  </xsl:template>

  <xsl:template match="input:staff/input:employee">
    <employee>
      <name>
        <xsl:value-of
          select="concat(input:first_name,' ',input:last_name)" />
      </name>
      <tel>
        <xsl:value-of
          select="input:telephones/*[@preferred = 'true']" />
      </tel>
    </employee>
  </xsl:template>

</xsl:stylesheet>



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
  xmlns:exception="xfield.exception.ValidationExceptionThrower"
  xmlns:input="cross-field-validation-namespace">

  <xsl:template name="validate">
    <xsl:for-each select="/input:staff/input:employee">
      <xsl:if
        test="input:gender = 'M' and input:title != 'Mr' 
               or input:gender = 'F' and input:title = 'Mr'">
        <xsl:value-of
          select="exception:throwException('Gender and title do not match')" />
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

 

XML Schema 可以使用枚举将传入的数据与一组预先决定的可接受的值进行比较。但是,如果引用数据是变量,且本身包含关系,那么 XML Schema 就无法对它进行验证。这个例子表明,使用 XSLT 和 Java 扩展的解决方案可以满足这种场景,并且可以对变量和外部 XML 数据集进行验证。注意,Schematron 也提供了这种功能,因为被转换的模式使用 XSLT 来执行验证。



<roles>
...
  <employee id="1234A" role="A"/>
  <employee id="1234D" role="A"/>
  <employee id="1234C" role="X"/>
  <employee id="1234B" role="Z"/>
  <employee id="1234X" role="Z"/>
...    
</roles>

这个例子中的外部引用数据是一组雇员,这些雇员有相关联的角色。检查传入的 XML 文档中的所有雇员 ID 是否都在引用数据集中。为此,使用 document() 函数将一个外部 XML 文档装载到一个变量中。检查是否至少有一个具有当前 ID 的雇员在 reference 变量中,以断言该雇员号是有效的。



<xsl:variable name="reference" select="document('reference.xml')" />

<xsl:template name="validate">
  <xsl:for-each select="/input:staff/input:employee">

    <xsl:if test="input:gender = 'M' and input:title != 'Mr' 
           or input:gender = 'F' and input:title = 'Mr'">
      <xsl:value-of
        select="exception:throwException(concat('Gender and title do not match 
               for employee ',input:first_name,' ',input:last_name))" />
    </xsl:if>

    <xsl:variable name="current_id" select="@id" />

    <xsl:if
      test="count($reference/roles/employee[@id = $current_id]) = 0">
      <xsl:value-of
        select="exception:throwException(concat('Invalid employee ID: ',$current_id))" />
    </xsl:if>

  </xsl:for-each>

</xsl:template>

 

总而言之,当处理同现约束,或者需要一个 XML 报告工具时,Schematron 是 XML Schema 的一个很好的补充。但是当性能更为重要时,尤其是当验证之后要进行转换时,XSLT 加 Java 扩展是一种更紧凑的解决方案。

:点击下载源代码

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

JAVA与XML 文档

  • 2012-05-08 23:19
  • 21.10MB
  • 下载

使用 SAX 处理 XML 文档

简介: 这里我将向大家介绍处理 XML 文档的另一个重要接口 SAX(Simple API for XML)。其中包括它的基本情况,它的 API,一个开发实例,实际开发中一些需注意的问题,以及它与 D...

使用 SQL 结果创建 XML 文档

使用 SQL 结果创建 XML 文档 算法 创建新 XML 文档的过程如下: 1 解析映射文件,以便获得必要的信息,包括要检索的数据在内。 2 检索源查询。这允许根据映射文件动态检索数据。 ...

使用 SAX 处理 XML 文档

使用 SAX 处理 XML 文档   王晓强,万千程序开发者中的一员,并乐在其中。热爱java和linux,一直利用java和xml相关技术进行应用开发,并在这些方面积累了丰富经验。可...

使用Delphi解析XML 文档

  • 2009-03-17 10:38
  • 26KB
  • 下载

如何用 Visual C#.net 中的 DTD、 XDR,或 XSD 验证 XML 文档

本文演示了如何将文档类型定义 (DTD)、 一个 Microsoft XML 数据缩减 (XDR) 架构或 XML 架构定义语言 (XSD) 架构应用到一个可扩展标记语言 (XML) 文档。本文还介绍...

使用 SAX 处理 XML 文档

SAX的基本情况 SAX同DOM一样也是一个访问XML文档的接口。SAX是Simple API for XML的缩写。它不像DOM那样是W3C的推荐标准。它是由XML-DEV邮件列表的成员开发维护,...

Qt 处理XML 文档的三种方式简介——DOM、SAX、流处理

XML(eXtensible Markup Language)是一种通用的文本格式,也叫可扩展标记语言。被广泛运用于数据交换和数据存储(虽然近年来 JSON 盛行,大有取代 XML 的趋势,但是对于一...

XML 文档定义的两种形式(DTD,SCHEMA)

XML DTD是目前使用比较广泛的一种XML模式,而XML SCHEMA是W3C官方推荐的XML定义格式。 DTD 指定:可以在文档中存在的元素、那些元素可以具有的属性、在元素内部元素的层次结构以及...
  • Baple
  • Baple
  • 2014-09-29 14:46
  • 1528
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)