简体   繁体   中英

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

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.

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'."

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM