Friday, November 27, 2009

XSD 1.1: another assertions example with Xerces-J !

Here's another XSD 1.1 assertions example, which I came up with today :)

An XML document is something like below:
  <person_db>
    <person id="1">
      <fname>john</fname>
      <lname>backus</lname>
      <dob>1995-12-10</dob>
    </person>
    <person id="2">
      <fname>rick</fname>
      <lname>palmer</lname>
      <dob>2001-11-09</dob>
    </person>
    <person id="3">
      <fname>neil</fname>
      <lname>cooks</lname>
      <dob>1998-11-10</dob>
    </person>
  </person_db>

Other than constraining the XML document to a structure like above, the XSD schema should specify following additional validation constraints, as well:
1) Each person's dob field should specify a date, which must be later than or equal to the date, 1900-01-01.
2) Each "person" element, should be sorted numerically according to "id" attribute, in an ascending fashion.

I wanted to achieve these validation objectives, completely with XSD 1.1 assertions. Here's the XSD 1.1 document, which I find that works fine, with Xerces-J:
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
 
     <xs:element name="person_db">
       <xs:complexType>
          <xs:sequence>
            <xs:element name="person" maxOccurs="unbounded" type="Person" />
          </xs:sequence>
          <xs:assert test="every $p in person[position() lt last()] satisfies
                            ($p/@id lt $p/following-sibling::person[1]/@id)" />
       </xs:complexType>
     </xs:element>
   
     <xs:complexType name="Person">
        <xs:sequence>
          <xs:element name="fname" type="xs:string" />
          <xs:element name="lname" type="xs:string" />
          <xs:element name="dob" type="xs:date" />
        </xs:sequence>
        <xs:attribute name="id" type="xs:int" use="required" />
        <xs:assert test="dob ge xs:date('1900-01-01')" />
     </xs:complexType>
  
   </xs:schema>

Notes: It also seems, that above XSD validation requirements could be met, with following changes as well:
1. Remove assertion from the complex type, "Person".
2. Have an additional assertion on the element, "person_db" which will now look something like following:
<xs:assert test="every $p in person[position() lt last()] satisfies
($p/@id lt $p/following-sibling::person[1]/@id)" />
<xs:assert test="every $p in person satisfies ($p/dob ge xs:date('1900-01-01'))" />

i.e, we'll now have two assertions on the element, "person_db" (which are actually specified on the element's schema type).

Though, I seem to like the first solution as it seems elegant to me, and more logically in place.

I am happy, that this particular example worked fine as I expected, with Xerces.

I hope that this post was useful.

3 comments:

Swati said...

Thanks for the helpful post! I'm in a bit of a spot here. I have something like this:

<xs:element name="Clients">
<xs:complexType>
<xs:sequence>
<xs:element ref="Client" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:element name="Client">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" type="xs:string" minOccurs="1" maxOccurs="1"/>

<xs:element name="ProfileInfo" type="ProfileInfoType" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:complexType name="ProfileInfoType">
<xs:sequence>
<xs:element name="ProfileUsed" type="ProfileNames" minOccurs="1" maxOccurs="1"/>
<xs:element name="Category" type="CategoryType" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:assert test="((@ProfileUsed eq 'Profile2') or (@ProfileUsed eq 'Profile1' and fn:count(./Category) > 0 ) or (@ProfileUsed eq 'Profile3'))"/>

</xs:complexType>

<xs:simpleType name="ProfileNames">
<xs:restriction base="xs:string">
<xs:enumeration value="Profile1"/>
<xs:enumeration value="Profile2"/>
<xs:enumeration value="Profile3"/>
</xs:restriction>
</xs:simpleType>

Now in my XML instance if I have multiple clients, this assertion works correctly for the first client, but fails for the second client.

Any idea why?

Swati said...

Please ignore the earlier comment. I figured out the problem.

Mukul Gandhi said...

I'm glad, you found the answer.