Aurora
Aurora

Reputation: 1354

Restrict xsd enumeration to one value for all occurrences inside a document

I would like to define an xsd schema which contains an enumeration. The enumeration's value should however be exclusive across the entire document. To illustrate this, consider the following schema:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="urn:enumtest" targetNamespace="urn:enumtest" elementFormDefault="qualified">
    <xs:simpleType name="Country">
        <xs:restriction base="xs:string">
            <xs:enumeration value="CH"/>
            <xs:enumeration value="AT"/>
            <xs:enumeration value="DE"/>
        </xs:restriction>
    </xs:simpleType>
    <xs:complexType name="Address">
        <xs:sequence>
            <xs:element name="Street" type="xs:string"/>
            <xs:element name="No" type="xs:integer"/>
            <xs:element name="Code" type="xs:integer"/>
            <xs:element name="Country" type="Country"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="Items">
        <xs:sequence>
            <xs:element name="Address" type="Address" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
    <xs:element name="Document">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Items" type="Items"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

The following two documents should pass validation because they each contain a unique value from the Country enumeration across the document (CH in first document, AT in second document):

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:enumtest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:enumtest enumtest.xsd">
    <Items>         
        <Address>
            <Street>Street1</Street>
            <No>1</No>
            <Code>1</Code>
            <Country>CH</Country>
            </Address>              
        <Address>
            <Street>Street2</Street>
            <No>2</No>
            <Code>2</Code>
            <Country>CH</Country>
        </Address>              
        <Address>
            <Street>Street3</Street>
            <No>3</No>
            <Code>3</Code>
            <Country>CH</Country>   
        </Address>              
    </Items>
</Document>

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:enumtest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:enumtest enumtest.xsd">
    <Items>         
        <Address>
            <Street>Street1</Street>
            <No>1</No>
            <Code>1</Code>
            <Country>AT</Country>
        </Address>              
        <Address>
            <Street>Street2</Street>
            <No>2</No>
            <Code>2</Code>
            <Country>AT</Country>
        </Address>              
        <Address>
            <Street>Street3</Street>
            <No>3</No>
            <Code>3</Code>
            <Country>AT</Country>   
        </Address>              
    </Items>
</Document>

The next document should be flagged as invalid since there is more than one value from the Country enumeration present in the document (CH and DE):

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:enumtest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:enumtest enumtest.xsd">
    <Items>         
        <Address>
            <Street>Street1</Street>
            <No>1</No>
            <Code>1</Code>
            <Country>CH</Country>
        </Address>              
        <Address>
            <Street>Street2</Street>
            <No>2</No>
            <Code>2</Code>
            <Country>CH</Country>
        </Address>              
        <Address>
            <Street>Street3</Street>
            <No>3</No>
            <Code>3</Code>
            <Country>DE</Country>
        </Address>              
    </Items>
</Document>

I already tried to experiment with a <unique> constraint, unfortunately without much success. Is there a way define such an exclusive enumeration per document?

Upvotes: 1

Views: 1447

Answers (2)

kjhughes
kjhughes

Reputation: 111630

XSD 1.0

You cannot constrain all Country elements to have the same one of several values simultaneously. Alternatives:

  • Your XSD could force all Country elements in a document to be a certain, preselected, value simultaneously via a fixed attribute.
  • Your XML could (arguably should) be redesigned to factor the common Country value out of each Address and stored instead in a single, common location in the document, such as an attribute or child element of Items or Document.

XSD 1.1

An xsd:assertion could express your constraint:

      <xs:assert test="count(distinct-values(en:Items/en:Address/en:Country)) = 1"/>

Here it is in the context of your complete XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:en="urn:enumtest" 
           targetNamespace="urn:enumtest" 
           elementFormDefault="qualified"
           xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" 
           vc:minVersion="1.1">
  <xs:simpleType name="Country">
    <xs:restriction base="xs:string">
      <xs:enumeration value="CH"/>
      <xs:enumeration value="AT"/>
      <xs:enumeration value="DE"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:complexType name="Address">
    <xs:sequence>
      <xs:element name="Street" type="xs:string"/>
      <xs:element name="No" type="xs:integer"/>
      <xs:element name="Code" type="xs:integer"/>
      <xs:element name="Country" type="en:Country"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Items">
    <xs:sequence>
      <xs:element name="Address" type="en:Address" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="Document">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Items" type="en:Items"/>
      </xs:sequence>
      <xs:assert test="count(distinct-values(en:Items/en:Address/en:Country)) = 1"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

Upvotes: 2

Michael Kay
Michael Kay

Reputation: 163360

It can obviously be done using assertions in XSD 1.1, but I don't think it can be done in XSD 1.0.

Upvotes: 0

Related Questions