Ключ XSD / keyref: иерархическая структура ключа - PullRequest
13 голосов
/ 21 мая 2009

Я пытаюсь определить некоторые ограничения внешнего ключа в схеме 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. Я попытался определить простую ключевую ссылку в корневом элементе для childKey, но это не сработало, поскольку я не вижу способа выбрать только дочерние элементы в соответствующем родительском поддереве. (Валидатор Eclipse, по крайней мере, всегда просто проверяет содержимое родительского поддерева last в документе ...).

Затем я попытался определить составной ключ (для root) с помощью:

  • селектор = родительский
  • field = @ parentKey
  • field = child / @ childKey

Сбой, если под родителем определено более одного потомка. Это правильное поведение, основанное на спецификации XSD 1.1 , раздел 3.11.4, пункт 3, в которой говорится, что ключ должен соответствовать не более одного узла на определение поля.

Просто повторюсь: если я заставлю childKeys быть глобально уникальными, это легко реализовать; Трудность заключается в том, чтобы сослаться на локально уникальных childKeys.

У каких-нибудь мастеров XSD есть идея?

Для справки приведен пример XSD с закомментированным ошибочным ключом childKey:

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

Ответы [ 3 ]

2 голосов
/ 21 мая 2009

Как насчет обращения к родителю от ребенка? Даже если много потомков, будет только один родитель, и объединение (parent, child) создает глобально уникальный ключ, даже если дочерний ключ уникален только внутри своего родителя:

  <key name="childKeyDef">
    <selector xpath="child"/>
    <field xpath="@childKey"/>
    <field xpath="../@parentKey"/>
  </key>

Это не работает в xmllint, хотя спецификация явно не запрещает это для полей - только для селекторов: 3.11.4 , (2) говорит, что селектор не может быть предком (это может быть только узел контекста или его потомки).

Ах, вот гвоздь в гробу (глядя на определенный синтаксис): допустимые выражения XPath очень ограничены и просто не включают ".." http://www.w3.org/TR/xmlschema-1/#c-fields-xpaths

Так что, извините, это не отвечает на ваш вопрос, но, возможно, это даст вам некоторые идеи.

1 голос
/ 22 мая 2009

Уродливое решение состоит в том, чтобы изменить ваш формат XML, чтобы parentKey включался в каждый дочерний элемент, например:

<parent>
  <child parentKey="parent1" childKey="child1"/>
  <child parentKey="parent1" childKey="child2"/>
</parent>

Я думаю, что ваша ситуация вполне законна, и я ожидаю, что найдется способ сделать это - почему бы не попробовать список рассылки xml-dev? Последний раз, когда я проверял, стало шумно, но некоторые из создателей xml все еще зависали.

0 голосов
/ 01 сентября 2009

У меня был похожий вопрос: Ключ 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>
...