XSD 1.1 - 子元素上的替代测试 Xpath

XSD 1.1 - Alternative Test Xpath on Child Element

我正在尝试使用 XSD 1.1 框架来测试当前作用域元素的子元素是否包含属性。如果为真,我想要一个元素的模式验证,如果为假,我想要另一个。

例子XML

<!-- XML 1 -->
<GrandParent name="Sam">
    <Parent name="Kevin">
        <Child name="Kyle" id="10" dob="1989-05-02"/>
    </Parent>
</GrandParent>


<!-- XML 2 -->
<GrandParent name="Sam">
    <Parent name="Kevin" id="10" dob="1975-10-11"/>
</GrandParent>

XSD 1.1 用于上述 xml 示例

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" >

  <xs:element name="GrandParent">
      <xs:complexType>
          <xs:sequence>
            <xs:element ref="Parent" />
          </xs:sequence>
          <xs:attribute name="name" type="xs:string" use="required" />
      </xs:complexType>
  </xs:element>

  <xs:element name="Parent">
      <xs:alternative test="./Child/@id" type="ParentType1" />
      <xs:alternative                    type="ParentType2" />
  </xs:element>

  <xs:element name="Child">
      <xs:complexType>
          <xs:attribute name="name" type="xs:string" use="required" />
          <xs:attribute name="id" type="xs:integer" use="required" />
          <xs:attribute name="dob" type="xs:date" use="required" />
      </xs:complexType>
  </xs:element>

  <xs:complexType name="ParentType1">
      <xs:sequence>
          <xs:element ref="Child" />
      </xs:sequence>
      <xs:attribute name="name" type="xs:string" use="required" />
  </xs:complexType>

  <xs:complexType name="ParentType2">
      <xs:attribute name="name" type="xs:string" use="required" />
      <xs:attribute name="id" type="xs:integer" use="required" />
      <xs:attribute name="dob" type="xs:date" use="required" />
  </xs:complexType>

</xs:schema>

我正在使用 Xerces xsd 1.1 验证功能 XSD 验证错误

XML 1
[Error] 1:46: cvc-complex-type.4: Attribute 'id' must appear on element 'Parent'.
[Error] 1:46: cvc-complex-type.4: Attribute 'dob' must appear on element 'Parent'.
[Error] 1:100: cvc-complex-type.2.1: Element 'Parent' must have no character or element information item [children], because the type's content type is empty.

XML 2
Validates

Xpath 对我来说似乎没有错,我尝试了一些变体,包括 //Child/@idboolean(Child/@id) 但没有任何乐趣。我不关心属性的值是什么,只关心它存在于子元素中。


更新

感谢 Michael Kay 在下面提供的答案,我重写了我的模式使用断言而不是替代方案。这是更新后的架构:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:element name="GrandParent">
        <xs:complexType>
            <xs:choice>
                <xs:element ref="Parent" />
            </xs:choice>
            <xs:attribute name="name" type="xs:string" use="required" />
        </xs:complexType>
    </xs:element>

    <xs:element name="Child">
        <xs:complexType>
            <xs:attribute name="name" type="xs:string"  use="required" />
            <xs:attribute name="id"   type="xs:integer" use="required" />
            <xs:attribute name="dob"  type="xs:date"    use="required" />
        </xs:complexType>
    </xs:element>

    <xs:element name="Parent">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="Child" minOccurs="0"/>
            </xs:sequence>
            <xs:attribute name="name" type="xs:string"  use="required" />
            <xs:attribute name="id"   type="xs:integer" use="optional" />
            <xs:attribute name="dob"  type="xs:date"    use="optional" />
            <xs:assert test="if (Child/@id) then not(@id or @dob) else (@id and @dob and not(Child))"/>
        </xs:complexType>
    </xs:element>

</xs:schema>

该模式似乎工作得更好,我用以下方法对其进行了测试XML 所有这些都按预期通过或失败

PASS <GrandParent name="Sam"><Parent name="Kevin"><Child name="Kyle" id="10" dob="1989-05-02"/></Parent></GrandParent>
PASS <GrandParent name="Sam"><Parent name="Kevin" id="10" dob="1975-10-11"/></GrandParent>
FAIL <GrandParent name="Sam"><Parent name="Kevin" id="10"/></GrandParent>
FAIL <GrandParent name="Sam"><Parent name="Kevin" dob="1975-10-11"/></GrandParent>
FAIL <GrandParent name="Sam"><Parent name="Kevin"/></GrandParent>

类型替代构造中的条件测试只能访问相关元素的属性,不能访问子元素或后代元素。

在规范中实现这一点的方法是定义 XPath 表达式是根据数据模型实例求值的,该数据模型实例构造为正在验证的元素的浅表副本,其中浅表副本包括属性的副本,但不包括子元素的副本.因此,尝试访问子项不会报错,只会报错。

限制的原因是为了避免如果您必须查看无效数据以确定其是否无效可能会出现的问题:您很容易陷入骗子悖论。

对于此验证,您需要更通用的断言功能,而不是类型替代。