I'm trying to define some foreign key constraints on an XML schema using xs:key and xs:keyref definitions. I want the structure of the document to be hierarchical in the following way:
<?xml version="1.0" encoding="UTF-8"?>
<tns:root xmlns:tns="http://www.example.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/ SampleSchema.xsd ">
<parent parentKey="parent1">
<child childKey="child1"/>
<child childKey="child2"/>
</parent>
<parent parentKey="parent2">
<child childKey="child1"/>
<child childKey="child2"/>
</parent>
<referrer parentRef="parent1" childRef="child2"/>
</tns:root>
A each parent has a (globally) unique key, defined by parentKey. Each child has key defined by childKey, but childKey is only unique within the scope of its containing parent.
There is then a list of referrers with foreign key references to a particular parent and child.
I'm able to define the keys as I want, simply by putting them on the correct element: the parentKey constraint on the root element, and the childKey constraint on the parent element. I can also define the keyref to parentKey without difficulty.
The problems arise when trying to define a keyref to childKey. I tried defining a simple keyref on the root element to childKey, but that doesn't work since I see no way to select only the child elements under the proper parent subtree. (The Eclipse validator, at least, always simply validates against the content of the last parent subtree in the document...).
I then tried defining a composite key (on root), with:
This fails if there is more than one child defined under the parent. That is the correct behavior based on the XSD 1.1 spec, section 3.11.4, item 3, which states that the key has to match at most one node per field definition.
Just to reiterate: if I force childKeys to be globally unique, this is easy to implement; the difficulty is around referencing locally unique childKeys.
Any XSD masters out there have an idea?
For reference, here is a sample XSD, with a failed childKey keyref commented out:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/" xmlns:tns="http://www.example.org/" elementFormDefault="unqualified">
<element name="root">
<complexType>
<sequence>
<element name="parent" maxOccurs="unbounded" minOccurs="1">
<complexType>
<sequence>
<element name="child" maxOccurs="unbounded" minOccurs="1">
<complexType>
<attribute name="childKey" type="string" use="required"/>
</complexType>
</element>
</sequence>
<attribute name="parentKey" type="string" use="required"/>
</complexType>
<key name="childKeyDef">
<selector xpath="child"/>
<field xpath="@childKey"/>
</key>
</element>
<element name="referrer" maxOccurs="unbounded" minOccurs="1">
<complexType>
<attribute name="parentRef" type="string"/>
<attribute name="childRef" type="string"/>
</complexType>
</element>
</sequence>
</complexType>
<key name="parentKeyDef">
<selector xpath="parent"/>
<field xpath="@parentKey"/>
</key>
<keyref name="parentKeyRef" refer="tns:parentKeyDef">
<selector xpath="referrers"/>
<field xpath="@parentRef"/>
</keyref>
<!-- <keyref name="childKeyRef" refer="tns:childKeyDef">-->
<!-- <selector xpath="referrers"/>-->
<!-- <field xpath="@childRef"/>-->
<!-- </keyref>-->
</element>
</schema>
An ugly solution is to change your XML format, so that the parentKey is included in each child, like this:
<parent>
<child parentKey="parent1" childKey="child1"/>
<child parentKey="parent1" childKey="child2"/>
</parent>
I think your situation is very legitimate, and I'd expect there to be a way to do this - why not try the xml-dev mailing list? It's become noisy last I checked, but some of the creators of xml were still hanging out there.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With