Restricción única XSD sobre el atributo de elementos hermanos de un tipo específico
Tengo un documento XML estructurado como Preguntas y respuestas que sigue el siguiente formato (editado para mayor claridad):
<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>
Mi XSD se ve así:
<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>
Por supuesto, hay más cosas de las que ves arriba, pero esto ilustra mi problema. Necesito que el id
atributo de answer
los elementos sea único entre hermanos. El XSD definido anteriormente impone la unicidad de id
los atributos entre elementos hermanos, pero no discrimina según el tipo de elemento. Probé una variedad de selectores y campos en la restricción única, pero no encontré una combinación que funcione. ¿Alguna sugerencia?
Simplemente cambia el selector a <xs:selector xpath="answer"/>
y estarás bien. En general, es bueno evitar XPath como .//*
, aunque sólo sea por motivos de rendimiento.
Este es el esquema XML para el ejemplo XML que proporcionó y que creo que funciona de la manera deseada:
<?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>
Su XML publicado se valida bien con lo anterior; duplicar la identificación de la respuesta de cualquier hermano produce un error de validación. Por ejemplo, el siguiente 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>
Cuando se valida (en QTAssistant, debería ser similar al mensaje en Visual Studio ya que está basado en la misma tecnología), estos son los errores:
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.
A continuación se muestra una captura de pantalla de Visual Studio 2010 que muestra la validación XML anterior con respecto al XSD que publiqué; Si bien los problemas se informan inadvertidamente como advertencias, de todos modos se informan.
Elegí al azar un validador en línea ( http://xsdvalidation.utilities-online.info/ ) y validé el mismo XML y XSD que publiqué; el error se informa como:
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".
Una cosa a la que debes prestar atención es cuando tienes un espacio de nombres de destino para tu XSD; en ese caso, es necesario definir un alias para todos los espacios de nombres involucrados y usarlos en sus selectores.
ACTUALIZACIÓN: Y el XSD con espacios de nombres:
<?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>
Observe la introducción del tns
prefijo y su uso en el selector.