XSD 1.1 - Alternative Test Xpath on Child Element

1.1k views Asked by At

Im trying to use the XSD 1.1 framework to test that the child element of the current scope element contains an attribute. If true I want one schema validation for the element, if false i want another.

Example 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 used for above xml examples

<?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>

Im using Xerces xsd 1.1 validation functionality XSD Validation Error

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

The Xpath doesn't seem wrong to me, i've tried a few variations including //Child/@id and boolean(Child/@id) but no joy. I dont care what the value of the attribute is, only that it exists in the child element.


Update

Thanks to the answer below provided by Michael Kay, I rewrote my schema use asserts instead of alternatives. Here is the updated schema:

<?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>

The schema seems to work much better, I tested it with the following XML all of which passed or failed as expected

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>
1

There are 1 answers

3
Michael Kay On BEST ANSWER

The condition tests in a type alternative construct can only access attributes of the element in question, not children or descendants.

The way this is achieved in the spec is by defining that the XPath expression is evaluated against a data model instance constructed as a shallow copy of the element being validated, where the shallow copy includes copies of attributes but not of children. As a result, attempting to access a child doesn't give an error, it just gives false.

The reason for the restriction is to avoid the problems that could occur if you have to look at invalid data in order to determine whether it is invalid: you can easily end up with a liar's paradox.

For this validation you need the more general capability of assertions, rather than type alternatives.