Doing More With XML Schemas (part 3) - Breaking The Mold
(Page 5 of 6 )
Next, how about introducing a referential integrity constraint similar to the one in the example above? Let's say we have a master list of available droid types, and only those droid types may be requisitioned for the various ships in the fleet. Here's my new XML document:
<?xml version="1.0" encoding="UTF-8"?>
<fleet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ship
name="Jabitha">
<communication>
<droid type="C3PO">4</droid>
</communication>
<repairs>
<droid
type="R2D2">6</droid>
<droid type="LE-BO2D9">1</droid>
</repairs>
<administration>
<droid
type="CZ-3">1</droid>
</administration>
</ship>
<ship name="Millennium
Falcon">
<communication>
<droid type="C3PO">1</droid>
</communication>
<repairs>
<droid
type="R2D2">1</droid>
<droid type="LE-BO2D9">4</droid>
</repairs>
<administration>
<droid
type="CZ-3">5</droid>
</administration>
</ship>
<droids>
<droid
type="R2D2">astromech droid</droid>
<droid type="C3PO">protocol droid</droid>
<droid
type="CZ-3">humanoid droid</droid>
<droid type="LE-BO2D9">repair droid</droid>
</droids>
</fleet>
There are two significant changes in this version of the XML document. First,
I have introduced a listing of droids which will act as a master list for all the droids on the ships of the fleet. To make things easier, I have further classified the droids on each ship into sections like administration, communication and repairs, based on their advertised functionality.
Now, all I need to do is update the schema to reflect this referential integrity constraint, in a manner similar to that in the previous example:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element
name="fleet">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="ship" type="shipType"
maxOccurs="unbounded"/>
<xsd:element name="droids"
type="droidsGroupType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:unique
name="NoRedefines">
<xsd:selector xpath="ship"/>
<xsd:field xpath="@name"/>
</xsd:unique>
<xsd:key
name="droidNameKey">
<xsd:selector xpath="droids/droid"/>
<xsd:field
xpath="@type"/>
</xsd:key>
</xsd:element>
<xsd:complexType name="droidType">
<xsd:simpleContent>
<xsd:extension
base="xsd:string">
<xsd:attribute name="type"
type="xsd:string"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType
name="shipType">
<xsd:sequence>
<xsd:element name="communication"
type="droidsGroupType"
maxOccurs="unbounded">
<xsd:keyref name="NoFakesCommunication"
refer="droidNameKey">
<xsd:selector
xpath="droid"/>
<xsd:field xpath="@type"/>
</xsd:keyref>
</xsd:element>
<xsd:element
name="repairs"
type="droidsGroupType" maxOccurs="unbounded">
<xsd:keyref
name="NoFakesRepair"
refer="droidNameKey">
<xsd:selector xpath="droid"/>
<xsd:field
xpath="@type"/>
</xsd:keyref>
</xsd:element>
<xsd:element name="administration"
type="droidsGroupType"
maxOccurs="unbounded">
<xsd:keyref name="NoFakesAdministration"
refer="droidNameKey">
<xsd:selector
xpath="droid"/>
<xsd:field xpath="@type"/>
</xsd:keyref>
</xsd:element>
</xsd:sequence>
<xsd:attribute
name="name" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="droidsGroupType">
<xsd:sequence>
<xsd:element
name="droid" type="droidType"
maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
First, I have to define a key for my master list of droids - the droid code will
do very well here.
<xsd:key name="droidNameKey">
<xsd:selector xpath="droids/droid"/>
<xsd:field
xpath="@type"/>
</xsd:key>
I can now reference this key within each functional category on each <ship>
via the <xsd:keyref> element:
<xsd:complexType name="shipType">
<xsd:sequence>
<xsd:element
name="communication"
type="droidsGroupType" maxOccurs="unbounded">
<xsd:keyref
name="NoFakesCommunication"
refer="droidNameKey">
<xsd:selector xpath="droid"/>
<xsd:field
xpath="@type"/>
</xsd:keyref>
</xsd:element>
<xsd:element
name="repairs"
type="droidsGroupType" maxOccurs="unbounded">
<xsd:keyref
name="NoFakesRepair"
refer="droidNameKey">
<xsd:selector xpath="droid"/>
<xsd:field
xpath="@type"/>
</xsd:keyref>
</xsd:element>
<xsd:element
name="administration"
type="droidsGroupType" maxOccurs="unbounded">
<xsd:keyref
name="NoFakesAdministration"
refer="droidNameKey">
<xsd:selector xpath="droid"/>
<xsd:field
xpath="@type"/>
</xsd:keyref>
</xsd:element>
</xsd:sequence>
<xsd:attribute
name="name" type="xsd:string"/>
</xsd:complexType>
As I have three functional groups for droids on each ship, the key reference
needs to be repeated thrice, one for each group (you could probably do this in a more efficient manner if you have a large number of groups - I leave that to you as an exercise, preferring this slightly clunkier option for illustrative purposes). The "refer" attribute of each <xsd:keyref> element links this constraint to the droid master list via the unique label "droidNameKey".
And that's it! You now have two constraints in a single schema definition, one ensuring that ship names are unique, and the other ensuring that only valid droids appear in each ship. Try it out and see for yourself!
Next: Two For One >>
More XML Articles
More By Harish Kamath, (c) Melonfire