Search code examples
xmlxsdxsd-validation

How to use keyref on nested element tag


What I want to validate:

<root xsi:noNamespaceSchemaLocation="test.xsd" xmlns="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
    <A>
      <a id="ID-1"/>
      <a id="ID-2"/>
    </A>
    <BBB>
      <b>
        <bb>
            <bbb idref="ID-1"></bbb>
        </bb>
      </b>
    </BBB>
</root>

Here is my .xsd file:

  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >

  <xsd:element name="root">
    <xsd:complexType>
      <xsd:all minOccurs="1" maxOccurs="1">
        <xsd:element name="A" type="myA"/>
        <xsd:element name="BBB" type="myBBB"/>
      </xsd:all>
    </xsd:complexType>
    <xsd:key name="myId">
      <xsd:selector xpath="./A/a"/>
      <xsd:field xpath="@id"/>
    </xsd:key>

    <xsd:keyref name="myIdref" refer="myId">
      <xsd:selector xpath="./BBB/b/bb/bbb"/>
      <xsd:field xpath="@idref"/>
    </xsd:keyref>
  </xsd:element>

  <xsd:complexType name="myA">
    <xsd:sequence minOccurs="1">
      <xsd:element name="a">
        <xsd:complexType>
          <xsd:attribute name="id" type="xsd:ID"/>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>

  <xsd:complexType name="myBBB">
    <xsd:sequence minOccurs="1">
      <xsd:element name="b">
        <xsd:complexType>
          <xsd:all>
              <xsd:element name="bb">
                  <xsd:complexType>
                      <xsd:sequence>
                          <xsd:element name="bbb">
                              <xsd:complexType>
                                  <xsd:simpleContent>
                                      <xsd:extension base="xsd:string">
                                          <xsd:attribute name="idref" type="xsd:ID"></xsd:attribute>
                                      </xsd:extension>
                                  </xsd:simpleContent>
                              </xsd:complexType>
                          </xsd:element>
                      </xsd:sequence>
                  </xsd:complexType>
              </xsd:element>
          </xsd:all>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

However I am getting:

element bbb: Schemas validity error : Element 'bbb', attribute 'idref':
Warning: No precomputed value available, the value was either invalid or something strange happend.

I tried using xsd:NCName but that didn't change anything. If I use ID-3 which is not yet defined, I get

No match found for key-sequence ['ID-3'] of keyref 'myIdref'.

I want to reference the id of the A element with idref of the bbb element. Is my xPath wrong?
Should I not use xsd:ID for keyRef?


Solution

  • I guess that your problem is that you want the bbb's element attribute type to be xsd:IDREF rather than xsd:ID. Then it would correspond with your xsd:ID definition:

    <xsd:attribute name="idref" type="xsd:IDREF"></xsd:attribute>
    

    A minor fix to add would be adding a maxOccurs="unbounded" attribute to the

    ...
    <xsd:complexType name="myA">
        <xsd:sequence minOccurs="1" maxOccurs="unbounded">
        ...
    

    So, the whole XSD could be

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
    
      <xsd:element name="root">
        <xsd:complexType>
          <xsd:all minOccurs="1" maxOccurs="1">
            <xsd:element name="A" type="myA"/>
            <xsd:element name="BBB" type="myBBB"/>
          </xsd:all>
        </xsd:complexType>
        <xsd:key name="myId">
          <xsd:selector xpath="./A/a"/>
          <xsd:field xpath="@id"/>
        </xsd:key>
    
        <xsd:keyref name="myIdref" refer="myId">
          <xsd:selector xpath="./BBB/b/bb/bbb"/>
          <xsd:field xpath="@idref"/>
        </xsd:keyref>
      </xsd:element>
    
      <xsd:complexType name="myA">
        <xsd:sequence minOccurs="1" maxOccurs="unbounded">
          <xsd:element name="a">
            <xsd:complexType>
              <xsd:attribute name="id" type="xsd:ID"/>
            </xsd:complexType>
          </xsd:element>
        </xsd:sequence>
      </xsd:complexType>
    
      <xsd:complexType name="myBBB">
        <xsd:sequence minOccurs="1">
          <xsd:element name="b">
            <xsd:complexType>
              <xsd:all>
                  <xsd:element name="bb">
                      <xsd:complexType>
                          <xsd:sequence>
                              <xsd:element name="bbb">
                                  <xsd:complexType>
                                      <xsd:simpleContent>
                                          <xsd:extension base="xsd:string">
                                              <xsd:attribute name="idref" type="xsd:IDREF"></xsd:attribute>
                                          </xsd:extension>
                                      </xsd:simpleContent>
                                  </xsd:complexType>
                              </xsd:element>
                          </xsd:sequence>
                      </xsd:complexType>
                  </xsd:element>
              </xsd:all>
            </xsd:complexType>
          </xsd:element>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>
    

    Now your XSD should validate your XML.
    To make this work, you don't even need the

    <xsd:key name="myId">
      <xsd:selector xpath="./A/a"/>
      <xsd:field xpath="@id"/>
    </xsd:key>
    
    <xsd:keyref name="myIdref" refer="myId">
      <xsd:selector xpath="./BBB/b/bb/bbb"/>
      <xsd:field xpath="@idref"/>
    </xsd:keyref>
    

    because this functionality is implied in the xsd:ID/xsd:IDREF code. xs:keyref is more flexible, but in your example, it is not needed.