Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSD unique constraint on attribute of sibling elements of a specific type

I have an XML document structured as Q&A which follows the following format (edited for clarity):

<question>
    <answer id="1">
        <question>
            <answer id="1"/>
            <answer id="2"/>
            <answer id="3"/>
        </question>
    </answer>
    <answer id="2">
        <question>
            <answer id="1"/>
            <answer id="2"/>
        </question>
    </answer>
</question>

My XSD looks like this:

<xs:element name="question">
     <xs:complexType>
        <xs:sequence>
            <xs:element name="answer" type="answerType" minOccurs="2" maxOccurs="unbounded">
            </xs:element>
        </xs:sequence>
    </xs:complexType>
    <xs:unique name="AnswerIdUnique">
        <xs:selector xpath="./*" />
        <xs:field xpath="@id" />
    </xs:unique>
</xs:element>

<xs:complexType name="answerType">
    <xs:sequence>
        <xs:element ref="question" minOccurs="0" maxOccurs="1" />
    </xs:sequence>
    <xs:attribute name="id" type="xs:token" use="required" />
</xs:complexType>

There is, of course, more to it than what you see above but this illustrates my problem. I need for the id attribute on answer elements to be unique among siblings. The XSD defined above enforces uniqueness of id attributes among sibling elements, but it does not discriminate on element type. I've tried a variety of selectors and fields in the unique constraint, but have not found a combination that works. Any suggestions?

like image 260
joelmdev Avatar asked Apr 27 '12 19:04

joelmdev


1 Answers

Just change the selector to <xs:selector xpath="answer"/> and you'll be fine. In general it is good to avoid XPaths like .//*, if only for performance reasons.

This is the XML Schema for the XML sample you've provided that I think is working the way you want:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="question" type="questionType">
        <xs:unique name="AnswerIdUnique">
            <xs:selector xpath="answer"/>
            <xs:field xpath="@id"/>
        </xs:unique>
    </xs:element>
    <xs:complexType name="questionType">
        <xs:sequence>
            <xs:element name="answer" type="answerType" minOccurs="2" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="answerType">
        <xs:sequence>
            <xs:element ref="question" minOccurs="0" maxOccurs="1"/>
        </xs:sequence>
        <xs:attribute name="id" type="xs:token" use="required"/>
    </xs:complexType>
</xs:schema>

Your posted XML validates fine with the above; duplicating any sibling answer's id yields a validation error. For example, the following XML:

<question> 
    <answer id="1"> 
        <question> 
            <answer id="1"/> 
            <answer id="2"/> 
            <answer id="1"/> 
        </question> 
    </answer> 
    <answer id="1"> 
        <question> 
            <answer id="1"/> 
            <answer id="2"/> 
        </question> 
    </answer> 
</question> 

When validated (in QTAssistant, should be similar to the message in Visual Studio since it is based on the same technology), these are the errors:

Error occurred while loading [], line 6 position 5
There is a duplicate key sequence '1' for the 'AnswerIdUnique' key or unique identity constraint.
Error occurred while loading [], line 9 position 3
There is a duplicate key sequence '1' for the 'AnswerIdUnique' key or unique identity constraint.
Document1.xml is invalid.

Below is a screenshot from Visual Studio 2010 showing the above XML validation against the XSD I've posted; while the problems are inadvertently reported as warnings, they are, nonetheless, reported.

VS2010 showing unique constraint errors

I've randomly picked an online validator (http://xsdvalidation.utilities-online.info/) and validated the same XML and XSD I've posted; the error is reported as:

org.xml.sax.SAXParseException: Duplicate unique value [1] declared for identity constraint of element "question".org.xml.sax.SAXParseException: Duplicate unique value [1] declared for identity constraint of element "question".

One thing you have to pay attention to is when you have a target namespace for your XSD; in that case, it is needed to define an alias for all of the involved namespaces, and use them in your selectors.

UPDATE: And the XSD with namespaces:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://localhost" xmlns="http://localhost" targetNamespace="http://localhost" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="question" type="questionType">
        <xs:unique name="AnswerIdUnique">
            <xs:selector xpath="tns:answer"/>
            <xs:field xpath="@id"/>
        </xs:unique>
    </xs:element>
    <xs:complexType name="questionType">
        <xs:sequence>
            <xs:element name="answer" type="answerType" minOccurs="2" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="answerType">
        <xs:sequence>
            <xs:element ref="question" minOccurs="0" maxOccurs="1"/>
        </xs:sequence>
        <xs:attribute name="id" type="xs:token" use="required"/>
    </xs:complexType>
</xs:schema>

Please notice the introduction of the tns prefix and the use of it in the selector.

like image 167
Petru Gardea Avatar answered Nov 19 '22 10:11

Petru Gardea