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?
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.
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.
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