Выразить рекурсивную ссылку в наборе повторяющихся упорядоченных элементов

Я пытаюсь написать схему RelaxNG, которая имеет следующие правила:

  1. line элемент может содержать ноль или более a а также b элементы.
  2. каждый a элемент должен иметь соответствующий b стихия и наоборот.
  3. a элементы всегда должны предшествовать их соответствию b элементы.

Таким образом, следующие все должны считаться действительными:

<line><a/><b/></line>

<line><a/><a/><b/><b/></line>

<line><a/><a/><b/><a/><b/><b/></line>

Между тем, все следующие недействительны:

<line><b/><a/></line>

<line><a/><a/><b/></line>

<line><a/></line>

<line><b/></line>

Как это можно выразить в RelaxNG? Моей первой мыслью было создать рекурсивную ссылку, вот так:

element line { pair* }+

pair = a, pair?, b

a = element a { empty }
b = element b { empty }

Однако Цзин считает это "плохой рекурсивной ссылкой на" пару "". Я не могу на всю жизнь понять, как это решить! Есть идеи?

1 ответ

Решение

Шаблоны Relax NG (как и модели содержимого DTD и XSD) являются по существу регулярными выражениями; они определяют обычные языки по набору имен элементов и text, Соответствие a/b, которое вы, похоже, ищете, требует контекстно-свободной грамматики; поэтому его нельзя определить в Relax NG.

Это, конечно, можно приблизить. Если вы ожидаете, что на практике у вас никогда не будет более пяти пар a/b (или, точнее, никогда не будет больше пяти) a элементы, для которых вы еще не видели соответствия b), вы можете определить любой из следующих языков.

Первый:

pair0 = (a, b)*
pair1 = (a, pair0, b)*
pair2 = (a, pair1, b)*
pair3 = (a, pair2, b)*
pair4 = (a, pair3, b)*
pair5 = (a, pair4, b)*
pair6 = (a, pair5, b)*
pair7 = (a, pair6, b)*
pair8 = (a, pair7, b)*
pair9 = (a, pair8, b)*
pair  = (a, pair9, b)*

Это определяет подмножество вашего языка, которое строго соблюдает ваши два правила, но не может обрабатывать пары a/b, вложенные более чем в 10 глубин. (Или 11, в зависимости от того, что вы рассчитываете.) Каждый документ, принятый по этому определению, будет членом языка, который вы на самом деле хотите, но не каждый член языка будет принят.

Если отклонение допустимых экземпляров языка неприемлемо, вы можете определить шаблоны, как указано выше, но переопределить pair0 как:

pair0 = (a, (a|b)*, b)*

Это определяет надмножество языка, в котором правила применяются для пар a/b вплоть до максимального уровня вложенности, но в которых правила отменяются, когда уровень вложенности превышает этот максимум. В этом случае, каждый член желаемого языка будет принят, но и мусор, который не должен быть.

Будут ли эти приближения использоваться в вашем приложении, я оставляю вам решать.

Если аппроксимации неприемлемы, вам может быть проще получить то, что вам нужно, определив XML по-другому.

Одно простое изменение будет заключаться в обтекании каждого соответствия a а также b в элементе (который я назову e):

element line { pair* }
a = element a { empty }
b = element b { empty }
pair = element e { a, pair*, b }

Теперь действительность документа обеспечивает очень простую гарантию того, что a элементы и b элементы спариваются, как требуется, и что каждый a предшествует его сопоставлению b,

Учитывая, что a а также b пусты, что каждый a сразу же следует за стартовой меткой для eи что каждый b непосредственно предшествует конечному тегу для того же e элемент, вы можете заменить a а также b элементы полностью с начальными тегами для e и конечные метки для eсоответственно и объявляю line как

element line { e* }
e = element e { e* }

Интерфейсы SAX, DOM-интерфейсы, XSLT, XQuery и все другие известные мне методы обработки XML упрощают сопоставление действий с началом и концом элемента примерно так же, как и сопоставление действий с пустым элементом.

Ваши действительные примеры становятся:

<line><e></e></line>
<line><e><e></e></e></line>
<line><e><e></e><e></e></e></line>

и ваши недействительные примеры становятся ненадлежащими данными.

Другие вопросы по тегам