Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I restrict the values of an XML attribute based on attribute values in other elements?

Tags:

xml

xpath

xsd

I have XML elements like this:

<characteristic name="Length"... />
<characteristic name="Width"... />
<!-- etc. -->

Is it possible to restrict the value of another attribute to be one of the values of (XPath) "//characteristic/@name"?

So this would be allowed:

<widget>
    <characteristic name="Length">100</characteristic>
</widget>

But this would not be allowed:

<widget>
    <characteristic name="Bananas">33</characteristic>
</widget>

(Because "Bananas" is not one of the names characteristics.)

I assume this can be done using "key" and "keyref", but I'm not sure how to implement this when both the key and the ref are attribute values.

Also, how would I do this if the characteristics were listed in a different XML document to the widgets? (Is that even possible?) Ideally I'd like to use XSD 1.0, but I'd be interested to know if XSD 1.1 adds anything useful here.

like image 393
Allan Lewis Avatar asked Dec 06 '25 06:12

Allan Lewis


2 Answers

Here is a sample schema:

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

  <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="characteristics">
          <xs:complexType>
            <xs:sequence maxOccurs="unbounded">
              <xs:element name="characteristic">
                <xs:complexType>
                  <xs:attribute name="name" type="xs:string"/>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="widgets">
          <xs:complexType>
            <xs:sequence maxOccurs="unbounded">
              <xs:element name="widget">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="characteristic">
                      <xs:complexType>
                        <xs:simpleContent>
                          <xs:extension base="xs:integer">
                            <xs:attribute name="name" type="xs:string"/>
                          </xs:extension>
                        </xs:simpleContent>
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
    <xs:key name="cname">
      <xs:selector xpath="characteristics/characteristic"/>
      <xs:field xpath="@name"/>
    </xs:key>
    <xs:keyref name="cname-ref" refer="cname">
      <xs:selector xpath="widgets/widget/characteristic"/>
      <xs:field xpath="@name"/>
    </xs:keyref>
  </xs:element>

</xs:schema>

When applied to the instance

<root>
  <characteristics>
    <characteristic name="Length"/>
    <characteristic name="Width"/>
  </characteristics>
  <widgets>
    <widget>
        <characteristic name="Length">100</characteristic>
    </widget>

    <widget>
        <characteristic name="Bananas">33</characteristic>
    </widget>
  </widgets>
</root>

Xerces reports the error you want: "test2014010301.xml:15:cvc-identity-constraint.4.3: Key 'cname-ref' with value 'Bananas' not found for identity constraint of element 'root'."

like image 181
Martin Honnen Avatar answered Dec 08 '25 23:12

Martin Honnen


In XSD 1.0, you probably can't do this (but I'm not sure, because I'm not sure how the "controlled" characeteristics relate to the "controlling" characteristics: it might be possible with key/keyref as you suggest).

In XSD 1.1, it can be done using assertions or conditional type assignment.

XSD 1.1 is currently implemented in Saxon, Xerces, and Altova.

like image 44
Michael Kay Avatar answered Dec 09 '25 00:12

Michael Kay