7

I am trying to create a schema definition using XSD 1.1 in which outcome of one element is dependent on other. For example, I have drop-down for list of countries and list of states for each country. When a person selects a country, only the states of that country can be selected. The pseudo-code of what I am trying to attain looks something like this.

<xs:schema xmlns:ie="http://www.interviewexchange.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="country">       
    <xs:simpleType>
        <xs:restriction base="xs:string">
            <xs:enumeration value="USA" />
            <xs:enumeration value="UK" />
            <xs:enumeration value="India" />
        </xs:restriction>
    </xs:simpleType>
</xs:element>
<xs:element name="state">       
    <xs:simpleType>
        <xs:restriction base="xs:string">
            <assert test="if (country eq 'USA')">
            <xs:enumeration value="MA" />
            <xs:enumeration value="AR" />
            <xs:enumeration value="NY" />
            </assert">
            <assert test="if (country eq 'India')">
            <xs:enumeration value="AP" />
            <xs:enumeration value="TN" />
            <xs:enumeration value="MP" />
            </assert">
        </xs:restriction>
    </xs:simpleType>
</xs:element>

Please suggest me whether I am following the right approach, If I am following the right approach, can anyone give me the code of how this restriction can be attained? Thanks in Advance...

Petru Gardea
  • 21,373
  • 2
  • 50
  • 62
Adithya Puram
  • 303
  • 2
  • 6
  • 23
  • This question may be of help http://stackoverflow.com/questions/13308585/xsd-schemas-enumeration-based-on-value-in-document/13313354#13313354 – nine9ths Dec 01 '12 at 05:28
  • That solution was using schematron and was using comma separated values in place of enumeration. Can you please provide a solution which uses the features added in XSD 1.1 like 'assert test' without using schematron and takes enumeration instead of comma separated values. Thank you. – Adithya Puram Dec 01 '12 at 18:57
  • Sorry, missed the 1.1 part, I haven't used 1.1 yet but from reading [spec](http://www.w3.org/TR/xmlschema11-1/#cAssertions), it looks like you can't use assertions and enumerations together like that, @test is simply an XPATH 2.0 that is evaluated in the context of the element being tested. – nine9ths Dec 01 '12 at 19:48

1 Answers1

10

You're getting close.

In XSD 1.1, assertions can only look down into the subtree, not up or over, so if you want to use assertions here, you will want to put them not in the type for 'state' but in the type for 'address':

<xs:element name="address">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="street"/>
      <xs:element ref="city"/>
      <xs:element ref="state" minOccurs="0"/>
      <xs:element ref="country"/>
    </xs:sequence>
    <xs:assert test="(country = 'UK' and not(state))
      or
      (country = 'US' and state = ('MA', 'AR', 'NY'))
      or
      (country = 'IN' and state = ('AP', 'TN', 'MP'))
      "/>
  </xs:complexType>
</xs:element>

A different approach (also in XSD 1.1) is to use conditional type assignment. This allows an element to be assigned different types based on XPath expressions which can refer to its elements (but not its children). If we move country and state to attributes (and then, for consistency, move street and city to attributes as well), we could use conditional type assignment this way. First, define the various simple types we want for state:

<xs:simpleType name="US-states">
  <xs:restriction base="xs:NMTOKEN">
    <xs:enumeration value="MA" />
    <xs:enumeration value="AR" />
    <xs:enumeration value="NY" />      
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="IN-states">
  <xs:restriction base="xs:NMTOKEN">
    <xs:enumeration value="AP" />
    <xs:enumeration value="TN" />
    <xs:enumeration value="MP" />   
  </xs:restriction>
</xs:simpleType>

Then define three different complex types for the three different kinds of addresses we want. I'm assuming for illustration that UK addresses don't get a 'state' attribute.

<xs:complexType name="US-address">
  <xs:attribute name="street" type="xs:string" use="required"/>
  <xs:attribute name="city" type="xs:string" use="required"/>
  <xs:attribute name="state" type="US-states"/>
  <xs:attribute name="country" type="xs:NMTOKEN" use="required"/>
</xs:complexType>
<xs:complexType name="UK-address">
  <xs:attribute name="street" type="xs:string" use="required"/>
  <xs:attribute name="city" type="xs:string" use="required"/>
  <xs:attribute name="country" type="xs:NMTOKEN" use="required"/>
</xs:complexType>
<xs:complexType name="IN-address">
  <xs:attribute name="street" type="xs:string" use="required"/>
  <xs:attribute name="city" type="xs:string" use="required"/>
  <xs:attribute name="state" type="IN-states"/>
  <xs:attribute name="country" type="xs:NMTOKEN" use="required"/>
</xs:complexType>

Now we bind the address element to the correct one of these based on the value of 'country':

<xs:element name="address">
  <xs:alternative test="@country='US'" type="US-address"/>
  <xs:alternative test="@country='IN'" type="IN-address"/>
  <xs:alternative test="@country='UK'" type="UK-address"/>
  <xs:alternative type="xs:error"/>
</xs:element>

The alternatives are tested in order, and the first one whose test evaluates to true assigns the type. The last alternative (without a test attribute) provides a default, which in this case is the error type (no elements or attributes are valid against the error type).

C. M. Sperberg-McQueen
  • 24,596
  • 5
  • 38
  • 65
  • 1
    If you want to constrain not just country/state value pairs, but country/state/city value triples, it's easier to extend the assertion-based solution than the conditional-type-assignment solution. The extension of the assertion to checking city values should be obvious: in addition to the constraint already expressed you want to check that for a given country/state pair the city values are drawn from a particular set. You can do that by rewriting the disjuncts: `(country='US' and state='MA' and city=('Boston', 'Worcester', 'Pittsfield', 'Northhampton','Springfield') or ...` – C. M. Sperberg-McQueen Dec 12 '12 at 21:57
  • Thank you very much C. M. Sperberg-McQueen – Adithya Puram Dec 12 '12 at 22:48
  • I have one last question for you, do you know of any java libraries that do XSD 1.1 validation other than Saxon(very expensive)? I am looking for a free one and the only free implementation is xercesImpl-2.11.0.jar which is still in its beta stage. – Adithya Puram Dec 12 '12 at 22:55
  • For XSD 1.1 your choices at present appear to be Saxon and Xerces J. – C. M. Sperberg-McQueen Dec 12 '12 at 23:46