XML 验证 - 如果那么

XML Validation - If Then

我正在尝试找到验证以下 XML 文档的最佳方法。到目前为止,我已经研究了三种方法:

DTD - 似乎没有足够的选择来实现它。

XSD - 几乎可以工作,但我似乎无法找到一种方法来做 If Then

XSL - 关于同样的问题,我可以做 If's,但我似乎无法想出一种方法来使其适用于此文档。

思路是这样的:根元素是<Answers>。下面是 1 个或多个 <Answer>。它们有一个 ID 属性,标记它对应的问题编号(在别处处理,不重要)。 每个问题可能有 0 个或多个答案,现在写为 <A1><A2>... 每个答案都有 1 个或多个元素,<E1><E2>...

示例文档如下所示:

<Answers>

  <Answer ID="1a">
    <A1>
      <E1>Element 1</E1>
      <E2>Element 2</E2>
      <E3>Element 3</E3>
      <E4>Element 4</E4>
    </A1>
    <A2>
      <E1>Element 1</E1>
      <E2>Element 2</E2>
      <E3>Element 3</E3>
      <E4>Element 4</E4>
    </A2>
  </Answer>

  <Answer ID="1b">
    <A1>
      <E1>Element A</E1>
      <E2>Element B</E2>
      <E3>Element C</E3>
    </A1>
  </Answer>

  <Answer ID="2">
    <A1>
      <E1>Element 1</E1>
      <E2>Element 2</E2>
      <E3>Element 3</E3>
      <E4>Element 4</E4>
      <E5>Element 5</E5>
    </A1>
    <A2>
      <E1>Element 1</E1>
      <E2>Element 2</E2>
      <E3>Element 3</E3>
      <E4>Element 4</E4>
      <E5>Element 5</E5>
    </A2>
    <A3>
      <E1>Element 1</E1>
      <E2>Element 2</E2>
      <E3>Element 3</E3>
      <E4>Element 4</E4>
      <E5>Element 5</E5>
    </A3>

  </Answer>
</Answers>

进入Elements的是任意字符串,内容不重要,只要不为空即可。

我需要验证的是,对于每个答案(用 ID 表示),每个 <A*>,都有一定数量的元素。在本例中,1a 有 4 个元素,1b 有 3 个,2 有 5 个。

格式很灵活,所以如果需要我可以将内容更改为: 元素A B元素 C元素 或者类似的东西。

我已经尝试了各种组合,但我似乎无法找到一种方法来根据答案 ID 要求 X 数量的 <E*>

有没有人有任何想法,或者他们能给我指出正确的方向,即使只是说 "Yes, XSD can do this" 或 "No, XSL cannot do this"?

您想要执行的验证类型("if then" 语句)称为 assertion。正如@kjhughes 所指出的,XML Schema 1.1 有断言,您可以在 XML 文档中使用 XSD 1.1.

实现此类复杂关系

或者,您可以使用 XSLT 转换,它并不真正执行验证,但在某种程度上,仅将输入 XML 转换为不满足约束的情况。不过,XSLT 不是一种验证语言。

我假设有人最终会提供 XSLT 答案 - 所以我想提出其他建议:Schematron。以下 Schematron 规则是断言,它们准确定义了您需要的约束。

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">

    <pattern>
        <title>Content of the answer element.</title>
        <rule context="Answer">
            <assert test="*[starts-with(name(),'A')]" xml:lang="en">The <name/> element must contain at least 1 A* element.</assert>
        </rule>
    </pattern>

    <pattern>
        <title>Count number of child elements E* of elements A*</title>
        <rule context="Answer[@ID = '1a']/*[starts-with(name(),'A')]">
            <assert test="count(*[starts-with(name(),'E')]) = 4" xml:lang="en">If the @ID attribute of the Answer element is "1a",
                then all child elements A* must have exactly 4 child elements E*.</assert>
        </rule>
        <rule context="Answer[@ID = '1b']/*[starts-with(name(),'A')]">
            <assert test="count(*[starts-with(name(),'E')]) = 3" xml:lang="en">If the @ID attribute of the Answer element is "1b",
                then all child elements A* must have exactly 3 child elements E*.</assert>
        </rule>
        <rule context="Answer[@ID = '2']/*[starts-with(name(),'A')]">
            <assert test="count(*[starts-with(name(),'E')]) = 5" xml:lang="en">If the @ID attribute of the Answer element is "2",
                then all child elements A* must have exactly 5 child elements E*.</assert>
        </rule>
    </pattern>

</schema>

如果输入文档包含以下结构:

<Answer ID="1a">
    <A1>
        <E1>Element 1</E1>
        <E2>Element 2</E2>
        <E4>Element 4</E4>
    </A1>
</Answer>

验证应用程序会抱怨,说

E [ISO Schematron] If the @ID attribute of the Answer element is "1a", then all child elements A* must have exactly 4 child elements E*.

Schematron 文件可以用 Schematron Reference Implementation - XSLT 样式表 - 或像 Oxygen 这样的环境来解释。


旁注:将上面的 SCH 规则转换为 XSLT 并不难,因为它们非常相似。您必须替换以下内容,例如:

SCH                                 | XSLT equivalent
-----------------------------------------------------------------------------
<sch:rule context="...">            | <xsl:template match="...">
<sch:assert test="*">               | <xsl:if test="not(*)">
<sch:report test="*">               | <xsl:if test="*">