XSD key/keyref: иерархическая структура ключа
Я пытаюсь определить некоторые ограничения внешнего ключа для схемы XML, используя определения xs: key и xs: keyref. Я хочу, чтобы структура документа была иерархической следующим образом:
<?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>
Каждый родитель имеет (глобально) уникальный ключ, определяемый parentKey. Каждый ребенок имеет ключ, определяемый childKey, но childKey является уникальным только в пределах его содержащего родителя.
Затем есть список ссылок с ссылками на внешние ключи для определенного родителя и дочернего элемента.
Я могу определить ключи, как я хочу, просто поставив их на правильный элемент: ограничение parentKey на корневой элемент и ограничение childKey на родительский элемент. Я также могу определить keyref для parentKey без труда.
Проблемы возникают при попытке определить keyref для childKey. Я попытался определить простой keyref в корневом элементе childKey, но это не работает, поскольку я не вижу способа выбрать только дочерние элементы под соответствующим родительским поддеревом. (Валидатор Eclipse, по крайней мере, всегда просто проверяет содержимое последнего родительского поддерева в документе...).
Затем я попытался определить составной ключ (в корне), используя:
- selector = parent
- поле = @parentKey
- field = child/@childKey
Это не удается, если для родителя установлено более одного дочернего элемента. Это правильное поведение, основанное на XSD 1.1 spec, раздел 3.11.4, пункт 3, в котором говорится, что ключ должен соответствовать не более одного node для определения поля.
Просто повторю: если я заставляю childKeys быть глобально уникальными, это легко реализовать; сложность связана с привязкой локально уникальных childKeys.
У всех мастеров XSD есть идея?
Для справки, вот пример XSD, с провалом childKey keyref:
<?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>
Ответы
Ответ 1
Как насчет ссылки на родителя от ребенка? Даже если у многих детей будет только один родитель, и объединение (родительский, дочерний) создает глобально уникальный ключ, хотя дочерний ключ только уникален в своем родителе:
<key name="childKeyDef">
<selector xpath="child"/>
<field xpath="@childKey"/>
<field xpath="../@parentKey"/>
</key>
Это не работает в xmllint, хотя спецификация явно не запрещает это для полей - только для селекторов: 3.11.4, (2) говорит, что селектор не может быть предком (это может быть только контекст node или потомки.)
А, вот гвоздь в гробу (смотря на конкретный синтаксис): допустимые выражения XPath очень ограничены и просто не включают ".." http://www.w3.org/TR/xmlschema-1/#c-fields-xpaths
Итак, извините, это не отвечает на ваш вопрос, но, возможно, это даст вам некоторые идеи.
Ответ 2
Уродливым решением является изменение формата XML, так что parentKey включается в каждый дочерний элемент, например:
<parent>
<child parentKey="parent1" childKey="child1"/>
<child parentKey="parent1" childKey="child2"/>
</parent>
Я думаю, что ваша ситуация очень законна, и я ожидаю, что это будет способ сделать это - почему бы не попробовать список рассылки xml-dev? Это стало шумным последним, что я проверил, но некоторые из создателей xml все еще висят там.
Ответ 3
У меня был аналогичный вопрос: XML-схема с несколькими полями
Я решил, что наилучшим подходом для меня было переупорядочить XML, чтобы позволить области определять локальность вместо того, чтобы принудительно вводить ключ с двумя полями.
В вашем сценарии, если вы переместите referrer внутри родителя, это позволит установить область для ссылки на соответствующий дочерний элемент. Затем вы должны были бы ссылаться на внешний объект на элемент, который ему нужно ссылаться.
Немного сложно определить, является ли это приемлемым решением, потому что ваша проблема немного абстрагируется. В моей проблеме, описанной в моем вопросе, я имел дело с вопросами, ответами и ответами пользователей. Первоначально я пытался проверить, действительно ли ответ пользователя был действительным ответом; мой первый подход включал ту же технику, которую вы используете. Мое окончательное решение включало перемещение ответа внутри вопроса, а затем обращение к пользователю.
Мой XML ПЕРЕД:
<?xml version="1.0" encoding="utf-8"?>
<survey>
<user id="bob">
<response questionIdRef="q101">yes</response>
<response questionIdRef="q102">white</response>
</user>
<user id="jane">
<response questionIdRef="q101">no</response>
<response questionIdRef="q102">blue</response>
</user>
<question id="q101">
<text>Do you like the color red?</text>
<answer>yes</answer>
<answer>no</answer>
</question>
<question id="q102">
<text>What is your favorite color?</text>
<answer>red</answer>
<answer>blue</answer>
<answer>white</answer>
<answer>yellow</answer>
</question>
</survey>
Мой XML ПОСЛЕ:
<?xml version="1.0" encoding="utf-8"?>
<survey>
<user id="bob" />
<user id="jane" />
<question id="q101">
<text>Do you like the color red?</text>
<answer>yes</answer>
<answer>no</answer>
<response userIdRef="bob">yes</response>
<response userIdRef="jane">no</response>
</question>
<question id="q102">
<text>What is your favorite color?</text>
<answer>red</answer>
<answer>blue</answer>
<answer>white</answer>
<answer>yellow</answer>
<response userIdRef="bob">white</response>
<response userIdRef="jane">blue</response>
</question>
</survey>