xs:unique in nested tags

This should cover W3C XML Schema, Relax NG and DTD related problems.
amsmota
Posts: 2
Joined: Fri Aug 12, 2005 5:29 pm

xs:unique in nested tags

Post by amsmota »

Hello all:

I was trying to define nodes with unique values, something like

Code: Select all


                <xs:unique name="nodeName">
<xs:selector xpath="Menu"/>
<xs:field xpath="Name"/>
</xs:unique>
that is working when my <Menu> nodes are on the same "level". However i have a nested structure where <Menu> can appear at any level, being child of other <Menu>s.

Since i can't use <xs:selector xpath="//Menu"/>, what cai i do? Or am i doomed to failure?

Thanks a lot.
george
Site Admin
Posts: 2095
Joined: Thu Jan 09, 2003 2:58 pm

Post by george »

Hi,

In XML Schema you can solve the problem if you know a maximum depth for your menus. Then you can have something like:

Code: Select all


<xs:unique name="MenuUnique">
<xs:selector xpath="Menu|Menu/Menu|Menu/Menu/Menu|Menu/Menu/Menu/Menu"/>
<xs:field xpath="Name"/>
</xs:unique>
This will work for a max depth of 4.

If you want to solve the general problem then embedding Schematron in XML Schema is your friend and the good news is that oXygen supports that.
See the sample schema and instance below:

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:sch="http://www.ascc.net/xml/schematron">
<xs:annotation>
<xs:appinfo>
<sch:title>Schematron validation schema for nested Menus</sch:title>
</xs:appinfo>
</xs:annotation>
<xs:element name="test">
<xs:complexType>
<xs:sequence>
<xs:element ref="Menu"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Menu">
<xs:annotation>
<xs:appinfo>
<sch:pattern name="Check that the menus are unique.">
<sch:rule context="Menu">
<sch:assert test="not(Name[.=following::Menu/Name])">Menu names should be
unique.</sch:assert>
</sch:rule>
</sch:pattern>
</xs:appinfo>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element ref="Name"/>
<xs:element ref="Menu" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Name" type="xs:string"> </xs:element>
</xs:schema>

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<?oxygen SCHSchema="test.xsd"?>
<test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="test.xsd">
<Menu>
<Name>M1</Name>
<Menu>
<Name>M2</Name>
<Menu>
<Name>M3</Name>
<Menu>
<Name>M4</Name>
<Menu>
<Name>M2</Name>
</Menu>
</Menu>
</Menu>
</Menu>
</Menu>
</test>
If you save the above files on your machine as test.xsd and test.xml then you can use the Validate action to validate the instance both against the XML Schema and against the embedded schematron rules.

Setting the context node in a Menu is important as you get the location of the error pointing to that element, in the above sample pointing to
/test/Menu[1]/Menu[1]

Best Regards,
George
amsmota
Posts: 2
Joined: Fri Aug 12, 2005 5:29 pm

Post by amsmota »

Thanks, i had figured out thye first option, but since there's at least 8 leves in my XML it seems a little clumsy. I wonder why the XPath Schema subset doesen't support the axis...

As per the second, while i'm developing in Oxygen the users probably use other tools, so i have to be generic. But it's a good reference for the future.

Thanks a lot.
Post Reply