XML-схема: keyref для иерархически более низкого элемента

Моя структура XML следующая:

ROOT
   |_ SetOfBandC (*)
   |               |_ SetOfB (1)
   |               |           |_ ElementB (*)
   |               |           |_ ElementB__Key 
   |               |_ SetOfC (1)
   |                           |_ ElementC (*)
   |                           |_ ElementC__Key 
   |_ ElementD (*)
   |
   |_ SetOfBandC__Key
   |_ ElementD__Key
   |
   |_ ElementD->SetOfBandC__Keyref
   |_ ElementD->ElementB__Keyref

В этой структуре у меня может быть несколько SetOfBandC, а также несколько ElementB и ElementC, но только 1 SetofB и SetOfC для каждого SetOfBandC.

Проблема в том, что ElementD имеет ключевую ссылку, ссылающуюся на конкретный SetOfBandC, а другой ссылается на ElementB из этого набора, но XML-валидатор при проверке достоверности ElementD->ElementB__Keyref ищет только в последнем SetOfBandC, а не в все из них или, лучше, в том, на который ссылается ElementD->SetOfBandC__Keyref. Поэтому, когда ElementD->ElementB__Keyref ссылается на ElementB, которого нет в последнем SetOfBandC, проверка не будет работать.

Вот мой ElementD->ElementB__Keyref:

<xs:keyref name="ElementD->ElementB__Keyref" refer="tns:ElementB__Key ">
    <xs:selector xpath="tns:ElementD" />
    <xs:field xpath="@elementD_ref" />
</xs:keyref>

И мой ElementB__Key:

<xs:key name="ElementB__Key">
    <xs:selector xpath="tns:ElementB" />
    <xs:field xpath="@elemB_name" />
</xs:key>

Где tns — мое целевое пространство имен. Что мне не хватает?

PS: имена в моем коде другие и в них нет ->.


person RVKS    schedule 17.01.2017    source источник
comment
выложи всю схему   -  person Sprotty    schedule 17.01.2017
comment
@Sprotty Я бы предпочел этого не делать, но я опубликовал это.   -  person RVKS    schedule 17.01.2017


Ответы (2)


Я думаю, что проблема в области действия ключей.

Насколько я понимаю, узел глобально идентифицируется не только по его имени узла, но и по имени nffg, которому он принадлежит. Таким образом, глобально это составной ключ: политика относится к узлу с именем nffg, а также с именем узла. Однако в текущей схеме keyRef в политиках относятся только к этим двум ключам независимо друг от друга, поэтому схема XML не знает, что они идут вместе.

Возможно, выходом было бы использовать глобально уникальные имена узлов, состоящие из префикса ключа узла с ключом nffg, например Nffg1-Node1, и использовать его вместо этого как одиночный keyref в политике. Если вы также хотите убедиться, что ссылка ссылается на исходный и целевой узлы в пределах одного и того же nffg, вам могут понадобиться два ключевых определения для node: одно глобальное для политики keyRefs (под allNffgsAndPolicies), одно local в nffg (под nffg) для ссылки keyRefs.

Несколько иной альтернативой является повторение атрибута nffgName в узлах и использование его вместе с неизмененным атрибутом nodeName в качестве глобального составного ключа для узлов с использованием двух элементов xs:field в ключе, которые можно использовать в политиках. Вы должны иметь возможность убедиться, что локальный атрибут узла nffgname совпадает с атрибутом родительского nffg с keys и keyRefs.

Дополнительный вопрос: он по-прежнему не объясняет, почему с исходным экземпляром и схемой Node3 отклоняется, а другие значения принимаются. Я ожидал, что ошибка будет вызвана значением, присутствующим в обоих поддеревьях. В спецификации конфликтующие ключи между дочерними элементами удаляются из таблицу узлов, однако в этом случае такой ключ, как Node2, будет конфликтующим, поскольку он есть в обоих поддеревьях, но не Node3, поскольку он появляется только в первом поддереве. Однако здесь происходит обратное, и кажется, что только последняя дочерняя таблица узлов рассматривается для включения в таблицу узлов верхнего уровня.

person Ghislain Fourny    schedule 18.01.2017
comment
Спасибо за хорошие идеи. К сожалению, я не могу изменить большую часть схемы, так как она уже используется файлами xml и даже некоторыми приложениями, было бы лучше изменить только ключ/keyrefs, чтобы проблемные, srcNodeRef и destNodeRef, работали. - person RVKS; 18.01.2017
comment
Спасибо за отзыв, РВКС. Я опубликовал еще один ответ, который должен соответствовать этому дополнительному ограничению. - person Ghislain Fourny; 18.01.2017

Если вы можете использовать XML-схему 1.1, следующее должно работать без модификации XML-документов или самой структуры схемы.

  • Удалите обе keyRef для tns:policy

  • Вместо этого используйте утверждение сложного типа для allNffgsAndPolicies:

    <xs:element name="allNffgsAndPolicies">
        <xs:complexType>
            <xs:sequence>
                <!-- same content as in original schema -->
            </xs:sequence>  
            <xs:assert test="every $i in tns:policy satisfies $i/@srcNode = tns:nffg/tns:nodes/tns:node/@nodeName and $i/@destNode = tns:nffg/tns:nodes/tns:node/@nodeName"/>
        </xs:complexType>
        <!-- other keys and keyRefs, only keep the first three -->
    </xs:element>
    

Я мог бы успешно протестировать это в oXygen: он проверяет исходный документ и не проходит проверку, если используются несуществующие имена исходного или целевого узла.

Чтобы активировать XML-схему 1.1, добавьте эти атрибуты в элемент xs:schema:

xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1"

Чтобы пройти лишнюю милю: если вдобавок к этому вы также хотите убедиться, что имя узла существует в nffg, на который ссылается политика с nffgRef, вы можете точно настроить утверждение:

<xs:assert test="
  every $i in tns:policy satisfies
  $i/@srcNode = tns:nffg[@nffgName eq $i/@nffgRef]/tns:nodes/tns:node/@nodeName
  and
  $i/@destNode = tns:nffg[@nffgName eq $i/@nffgRef]/tns:nodes/tns:node/@nodeName"/>
person Ghislain Fourny    schedule 18.01.2017
comment
К сожалению, я не могу использовать XML-схему 1.1. Но еще раз спасибо за вашу огромную поддержку! Вы думаете, что нет способа сделать это с keyrefs? Даже ваше первое решение, которое ищет узел во всех Nffgs, а не в указанном, будет в порядке. - person RVKS; 18.01.2017