Using xs:key and xs:unique As Co-occurrence Constraints

Co-occurrence constraints are interdependent conditions given on the child elements or attributes of a node, such as “if this element is present, then this attribute must be absent.” Under-implemented within W3C XML Schema, these constraints can be a workaround to the "Consistent Declaration rule,” which forbids definition of two different content models for an element at the same location in an instance document. With co-occurrence constraints, one can define a superset of the two content models and add the constraints to forbid the unwanted combinations. This is frequently useful with vocabularies (such as RDF) in which some properties can be expressed either as an attribute or an element, and in which we may want to extend our book example to accept the ISBN number and the title to be expressed as elements or attributes. The two following instance documents are then valid (with their two remaining combinations):

<book id="b0836217462" available="true">
  <isbn>
    0836217462
  </isbn>
  <title lang="en">
    Being a Dog Is a Full-Time Job
  </title>
  ../..
</book>

or:

<book id="b0836217462" available="true" isbn="0836217462"
  title="Beinga Dog Is a Full-Time Job">
  .../...
</book>

The obvious way is to define the book element as a choice between the four different valid content models (with the four combinations of elements and attributes). However, this is forbidden by the Consistent Declaration rule, which states that only one content model may be used for a given element. The workaround is to define a content model that accepts both optional elements and attributes:

<xs:complexType>
  <xs:sequence>
    <xs:element ref="isbn" minOccurs="0"/>
    <xs:element ref="title" minOccurs="0"/>
    <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element ref="character" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
  <xs:attribute ref="available"/>
  <xs:attribute ref="isbn"/>
  <xs:attribute ref="title"/>
</xs:complexType>

This definition allows instance documents with both a title (or isbn) element and attribute or instance documents without any title or isbn at all. We need to add co-occurrence constraints. In a more general case, these constraints cannot be expressed using W3C XML Schema, and we need to embed other languages (as shown in Chapter 14) but when we think about it, xs:unique , xs:key , and xs:keyref can be considered very specific co-occurrence constraints and they can be used here. If we want to insure that we have only one title and ISBN number, we can add a xs:key definition in the book element itself:

<xs:key name="isbn">
  <xs:selector xpath="."/>
  <xs:field xpath="isbn|@isbn"/>
</xs:key>
              
<xs:key name="title">
  <xs:selector xpath="."/>
  <xs:field xpath="title|@title"/>
</xs:key>

These keys are evaluated in the scope of a book element and won’t have any effect outside each book element. They will consider the book invalid if the XPath expression in their field element returns either no nodes or multiple nodes. Note that if we had used xs:unique instead of xs:key , we would still have required that only one of the elements or attributes be present, but that would have made the property optional. For the record, the full definition of our book element would then be:

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="isbn" minOccurs="0"/>
      <xs:element ref="title" minOccurs="0"/>
      <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/> 
      <xs:element ref="character" minOccurs="0"
        maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
    <xs:attribute ref="available"/>
    <xs:attribute ref="isbn"/>
    <xs:attribute ref="title"/>
  </xs:complexType>
  <xs:key name="isbn">
    <xs:selector xpath="."/>
    <xs:field xpath="isbn|@isbn"/>
  </xs:key>
  <xs:key name="title">
    <xs:selector xpath="."/>
    <xs:field xpath="title|@title"/>
  </xs:key>
</xs:element>