Saturday, June 20, 2009

Trouble with Schemas

I had to spend some time this week working on XML schemas. The trouble with schemas is that you can go all out and implement an almost object-oriented inheritance hierarchy. Which is exactly what I did.

I unwittingly set foot into the rarefied area of substitutionGroups and the like. And trying to find information when you don't know what to look for is a huge problem. So to save you the trouble and for my own future reference, here's what I learned. I could be wrong in several places, but it should be a good starting point for you.

This is what I was I had in mind. I needed a generic Object and a simple Collection-like container that could hold 1 or more Objects. And if you are not using XMLSpy...well good luck!

This is what the Object looks like:


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.javaforu.com/schemas/lesson1" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.javaforu.com/schemas/lesson1" elementFormDefault="qualified" attributeFormDefault="unqualified">

<xs:complexType name="GenericObjectType"></xs:complexType>

<xs:element name="GenericObject" type="GenericObjectType"></xs:element>

</xs:schema>


And this is what the Object Collection looks like:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.javaforu.com/schemas/lesson1" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.javaforu.com/schemas/lesson1" elementFormDefault="qualified" attributeFormDefault="unqualified">


<xs:complexType name="GenericObjectCollectionType">
<xs:sequence>
<xs:element ref="GenericObject" maxOccurs="unbounded"></xs:element>
</xs:sequence>
</xs:complexType>

<xs:element name="GenericObjectCollection" type="GenericObjectCollectionType"></xs:element>

</xs:schema>


That was easy enough. What I need now is a Person object that inherits from Object.


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.javaforu.com/schemas/lesson1" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.javaforu.com/schemas/lesson1" elementFormDefault="qualified" attributeFormDefault="unqualified">


<xs:complexType name="PersonType">
<xs:complexContent>
<xs:extension base="GenericObjectType">
<xs:sequence>
<xs:element name="firstName" type="xs:string"></xs:element>
<xs:element name="lastName" type="xs:string"></xs:element>
<xs:element name="age" type="xs:positiveInteger"></xs:element>
<xs:element name="gender" type="xs:string"></xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>

<xs:element name="Person" type="PersonType" substitutionGroup="GenericObject"></xs:element>

</xs:schema>


Note the use of substitutionGroup="GenericObject" in the Person element. This means that I can create Person tags/elements where ever GenericObject tags are allowed. That's very cool!

Here comes the clincher..If I need a special PersonCollection that only allows Person objects but still inherits from the GenericCollection, this is what I'd have to do:


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.javaforu.com/schemas/lesson1" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.javaforu.com/schemas/lesson1" elementFormDefault="qualified" attributeFormDefault="unqualified">


<xs:complexType name="PersonCollectionType">
<xs:complexContent>
<xs:restriction base="GenericObjectCollectionType">
<xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:element ref="Person"></xs:element>
</xs:choice>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>

</xs:schema>


Now, pay attention: For some reason, I'm yet to discover why, you cannot just replace the ref to the GenericObject with a Person ref when you have an unbounded occurrence rule. Try it. It has to be wrapped in a choice rule.

And of course the substitutionGroup="GenericObjectCollection" will you use the PersonCollection where ever the GenericCollection is allowed.

<xs:element name="PersonCollection" substitutionGroup="GenericObjectCollection"></xs:element>


This is what a sample looks XML like:

<?xml version="1.0" encoding="UTF-8"?>
<DocRoot xmlns="http://www.javaforu.com/schemas/lesson1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.javaforu.com/schemas/lesson1 Lesson1.xsd">

<GenericObject></GenericObject>

<Person>
<firstName>sdsfxcv</firstName>
<lastName>xsdf</lastName>
<age>12</age>
<gender>m</gender>
</Person>

<GenericObjectCollection>
<GenericObject></GenericObject>

<Person>
<firstName>asd</firstName>
<lastName>fgdfgd</lastName>
<age>77</age>
<gender>f</gender>
</Person>
</GenericObjectCollection>

<PersonCollection>
<Person>
<firstName>abcd</firstName>
<lastName>def</lastName>
<age>20</age>
<gender>m</gender>
</Person>
</PersonCollection>

</DocRoot>


And the full schema:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.javaforu.com/schemas/lesson1" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.javaforu.com/schemas/lesson1" elementFormDefault="qualified" attributeFormDefault="unqualified">

<xs:complexType name="GenericObjectType"/>

<xs:complexType name="GenericObjectCollectionType">
<xs:sequence>
<xs:element ref="GenericObject" maxOccurs="unbounded"></xs:element>
</xs:sequence>
</xs:complexType>

<xs:complexType name="DocRootType">
<xs:sequence>
<xs:element ref="GenericObject" minOccurs="0" maxOccurs="unbounded"></xs:element>
<xs:element ref="GenericObjectCollection" minOccurs="0" maxOccurs="unbounded"></xs:element>
</xs:sequence>
</xs:complexType>

<xs:element name="GenericObject" type="GenericObjectType"></xs:element>
<xs:element name="GenericObjectCollection" type="GenericObjectCollectionType"></xs:element>
<xs:element name="DocRoot" type="DocRootType"></xs:element>

<!---->

<xs:complexType name="PersonType">
<xs:complexContent>
<xs:extension base="GenericObjectType">
<xs:sequence>
<xs:element name="firstName" type="xs:string"></xs:element>
<xs:element name="lastName" type="xs:string"></xs:element>
<xs:element name="age" type="xs:positiveInteger"></xs:element>
<xs:element name="gender" type="xs:string"></xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>

<xs:complexType name="PersonCollectionType">
<xs:complexContent>
<xs:restriction base="GenericObjectCollectionType">
<xs:sequence>
<xs:choice maxOccurs="unbounded">
<xs:element ref="Person"></xs:element>
</xs:choice>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>

<xs:element name="Person" type="PersonType" substitutionGroup="GenericObject"></xs:element>
<xs:element name="PersonCollection" substitutionGroup="GenericObjectCollection"></xs:element>

</xs:schema>


If you find any mistakes or have any other such interesting bits of information to share, I'd love to hear it.

0 comments: