XSD - как разрешить элементы в любом порядке любое количество раз?

109

Я пытаюсь создать XSD и пытаюсь написать определение со следующим требованием:

  • Разрешить указанному дочернему элементу появляться любое количество раз (от 0 до неограниченного)
  • Разрешить дочерним элементам быть в любом порядке

Я осмотрелся и нашел различные решения вроде этого :

<xs:element name="foo">
  <xsl:complexType>
    <xs:choice minOccurs="0" maxOccurs="unbounded">
      <xs:element name="child1" type="xs:int"/>
      <xs:element name="child2" type="xs:string"/>
    </xs:choice>
  </xs:complexType>
</xs:element>

Но из того, что я понимаю, xs: choice по-прежнему позволяет выбирать только один элемент. Следовательно, установка MaxOccurs на неограниченное значение должно означать только то, что «любой один» из дочерних элементов может появляться несколько раз. Это точно?

Если приведенное выше решение неверно, как я могу достичь того, что я указал выше в моем требовании?

РЕДАКТИРОВАТЬ : Что делать, если требования следующие?

  • Элемент child1 child2 может появляться любое количество раз (от 0 до неограниченного)
  • Элементы должны быть в любом порядке
  • Элементы child3 и child4 должны появиться ровно один раз.

Например, этот xml действителен:

<foo>
<child1> value </child1>
<child1> value </child1>
<child3> value </child3>
<child2> value </child2>
<child4> value </child4>
<child1> value </child1>
</foo>

но это не (пропавший ребенок3)

<foo>
<child1> value </child1>
<child1> value </child1>
<child2> value </child2>
<child4> value </child4>
<child1> value </child1>
</foo>
jvtech
источник

Ответы:

62

В схеме, которую вы используете в своем вопросе, child1или child2может появляться в любом порядке, любое количество раз. Это похоже на то, что вы ищете.

Изменить: если вы хотите, чтобы только один из них появлялся неограниченное количество раз, вместо этого неограниченный должен был бы перейти к элементам:

Изменить: фиксированный тип в XML.

Изменить: Заглавная O в maxOccurs

<xs:element name="foo">
   <xs:complexType>
     <xs:choice maxOccurs="unbounded">
       <xs:element name="child1" type="xs:int" maxOccurs="unbounded"/>
       <xs:element name="child2" type="xs:string" maxOccurs="unbounded"/>
     </xs:choice>
   </xs:complexType>
</xs:element>
xcut
источник
в основном да, я ищу, чтобы элементы child1, child2 появлялись в любом порядке, любое количество раз .. предоставленный вами ответ работает только для одного элемента, верно? или это также решает мои требования?
jvtech
Схема в вашем вопросе соответствует вашим требованиям; альтернативная схема в моем ответе предназначена для одного элемента. Надеюсь, это проясняет ситуацию! :)
xcut
@Pavel, @xcut, спасибо за разъяснения, см. Отредактированное требование .. есть мысли?
jvtech
2
jvtech: вы не можете удовлетворить это отредактированное требование с помощью схемы XML; единственный способ добиться этого - если child3 и child4 могут появляться только в конце. В этом случае вам нужна последовательность, содержащая выбор, а затем два элемента.
xcut
1
@ Daij-Djan Я также обнаружил, что это не сработало. Попробуйте добавить maxOccurs = "unbounded" к элементу выбора, чтобы было разрешено более одного дочернего элемента.
MikeD
107

Альтернативная формулировка вопроса, добавленная в более позднем редактировании, кажется, все еще остается без ответа: как указать, что среди дочерних элементов элемента должен быть один именованный child3, один именованный child4и любое число с именем child1или child2, без ограничения на порядок в которые появляются у детей.

Это легко определяемый регулярный язык, и необходимая вам модель содержимого изоморфна регулярному выражению, определяющему набор строк, в которых каждая цифра 3 и 4 встречается ровно один раз, а цифры 1 и 2 'встречаются любое количество раз. Если не совсем понятно, как это писать, можно подумать, какой тип конечного автомата вы бы построили для распознавания такого языка. У него будет как минимум четыре различных состояния:

  • исходное состояние, в котором не было видно ни «3», ни «4»
  • промежуточное состояние, в котором было замечено «3», но не «4»
  • промежуточное состояние, в котором было замечено «4», но не «3»
  • конечное состояние, в котором были видны как '3', так и '4'

Независимо от того, в каком состоянии автомат находится, могут быть прочитаны «1» и «2»; они не меняют состояние машины. В исходном состоянии также принимаются цифры «3» или «4»; в промежуточных состояниях принимается только «4» или «3»; в конечном состоянии ни «3», ни «4» не принимаются. Структуру регулярного выражения легче всего понять, если мы сначала определим регулярное выражение для подмножества нашего языка, в котором встречаются только «3» и «4»:

(34)|(43)

Чтобы позволить «1» или «2» встречаться любое количество раз в данном месте, мы можем вставить (1|2)*(или [12]*если наш язык регулярных выражений принимает эту нотацию). Вставляя это выражение во все доступные места, мы получаем

(1|2)*((3(1|2)*4)|(4(1|2)*3))(1|2)*

Преобразовать это в модель контента несложно. Базовая структура эквивалентна регулярному выражению (34)|(43):

<xsd:complexType name="paul0">
  <xsd:choice>
    <xsd:sequence>
      <xsd:element ref="child3"/>
      <xsd:element ref="child4"/>
    </xsd:sequence>
    <xsd:sequence>
      <xsd:element ref="child4"/>
      <xsd:element ref="child3"/>
    </xsd:sequence>
  </xsd:choice>
</xsd:complexType>

Вставить ноль или более вариантов child1и child2просто:

<xsd:complexType name="paul1">
  <xsd:sequence>
    <xsd:choice minOccurs="0" maxOccurs="unbounded">
      <xsd:element ref="child1"/>
      <xsd:element ref="child2"/>
    </xsd:choice>      
    <xsd:choice>
      <xsd:sequence>
        <xsd:element ref="child3"/>
        <xsd:choice minOccurs="0" maxOccurs="unbounded">
          <xsd:element ref="child1"/>
          <xsd:element ref="child2"/>
        </xsd:choice>      
        <xsd:element ref="child4"/>
      </xsd:sequence>
      <xsd:sequence>
        <xsd:element ref="child4"/>
        <xsd:choice minOccurs="0" maxOccurs="unbounded">
          <xsd:element ref="child1"/>
          <xsd:element ref="child2"/>
        </xsd:choice>      
        <xsd:element ref="child3"/>
      </xsd:sequence>
    </xsd:choice>
    <xsd:choice minOccurs="0" maxOccurs="unbounded">
      <xsd:element ref="child1"/>
      <xsd:element ref="child2"/>
    </xsd:choice>      
  </xsd:sequence>
</xsd:complexType>

Если мы хотим немного минимизировать объем, мы можем определить именованную группу для повторяющихся вариантов child1и child2:

<xsd:group name="onetwo">
  <xsd:choice>
    <xsd:element ref="child1"/>
    <xsd:element ref="child2"/>
  </xsd:choice>   
</xsd:group>

<xsd:complexType name="paul2">
  <xsd:sequence>
    <xsd:group ref="onetwo" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:choice>
      <xsd:sequence>
        <xsd:element ref="child3"/>
        <xsd:group ref="onetwo" minOccurs="0" maxOccurs="unbounded"/>
        <xsd:element ref="child4"/>
      </xsd:sequence>
      <xsd:sequence>
        <xsd:element ref="child4"/>
        <xsd:group ref="onetwo" minOccurs="0" maxOccurs="unbounded"/>
        <xsd:element ref="child3"/>
      </xsd:sequence>
    </xsd:choice>  
    <xsd:group ref="onetwo" minOccurs="0" maxOccurs="unbounded"/>
  </xsd:sequence>
</xsd:complexType>

В XSD 1.1 allбыли сняты некоторые ограничения на -groups, поэтому эту модель содержимого можно определить более кратко:

<xsd:complexType name="paul3">
  <xsd:all>
    <xsd:element ref="child1" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:element ref="child2" minOccurs="0" maxOccurs="unbounded"/>
    <xsd:element ref="child3"/>
    <xsd:element ref="child4"/>      
  </xsd:all>
</xsd:complexType>

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

К.М. Сперберг-Маккуин
источник
3
Мне нравится XSD 1.0 xs: все альтернативы.
TWiStErRob
8
+1. Это отличный ответ, и он заслуживает большего количества голосов.
Christoffer Lette
1
Отличный ответ! Мне очень нравятся подобные объяснения. Он раскрывает всю логику и доводы в пользу достижения цели. Теперь я не только знаю, как решить эту проблему, но узнал новый подход к решению подобных проблем. Объяснение этого с помощью конечного автомата - очень хорошая идея.
egelev
3
Майкл, вы говорите, что «эти изменения для всех групп на самом деле не изменяют выразительную силу языка; они только делают определение некоторых видов языков более лаконичным». Но если обобщить проблему на любое количество дочерних элементов, подмножество которых может появляться один раз, а другое подмножество, которое может появляться сколько угодно раз, решение XSD 1.0 приведет к комбинаторному взрыву, не так ли? Пока решение XSD 1.1 останется чистым.
ebruchez 06
1
ebruchez, да - выразительная сила , как я использую этот термин, не то же самое, что лаконичность , компактность , лаконичность или управляемость . Выразительная сила только спрашивает: «Может ли этот формализм определить этот язык?» Он не спрашивает о размере грамматики или о том, сделает ли ее меньше синтаксический сахар. Комбинаторный взрыв, о котором вы упомянули, означает, что обработка больших наборов элементов без изменений XSD 1.1 для всех групп становится очень неприятной очень быстро (а для больших n может привести к истощению памяти). Это не значит, что они становятся невозможными в принципе.
CM Sperberg-McQueen
49

Вот что наконец сработало для меня:

<xsd:element name="bar">
  <xsd:complexType>
    <xsd:sequence>
      <!--  Permit any of these tags in any order in any number     -->
      <xsd:choice minOccurs="0" maxOccurs="unbounded">
        <xsd:element name="child1" type="xsd:string" />
        <xsd:element name="child2" type="xsd:string" />
        <xsd:element name="child3" type="xsd:string" />
      </xsd:choice>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>
Алан
источник
5
В самом деле, хитрость заключается в использовании xsd: choice с квантификаторами <xsd: choice minOccurs = "0" maxOccurs = "
unbounded
6
Я думаю, стоит отметить, что приведенный выше пример работает даже без элемента последовательности, включающего элемент выбора.
9

Но из того, что я понимаю, xs: choice по-прежнему позволяет выбирать только один элемент. Следовательно, установка MaxOccurs на неограниченное значение должно означать только то, что «любой один» из дочерних элементов может появляться несколько раз. Это точно?

Нет. Выбор происходит индивидуально для каждого «повторения» xs:choiceпроисходящего по причине maxOccurs="unbounded". Следовательно, опубликованный вами код верен и действительно будет делать то, что вы хотите, как написано.

Павел Минаев
источник
Ваш комментарий с ответом @Alan прекрасно все это объясняет.
бор
3

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

  <xs:element name="foo">
    <xs:complexType>
      <xs:sequence minOccurs="0" maxOccurs="unbounded">
        <xs:choice>
          <xs:element maxOccurs="unbounded" name="child1" type="xs:unsignedByte" />
          <xs:element maxOccurs="unbounded" name="child2" type="xs:string" />
        </xs:choice>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

Это позволит вам создать такой файл, как:

<?xml version="1.0" encoding="utf-8" ?>
<foo>
  <child1>2</child1>
  <child1>3</child1>
  <child2>test</child2>
  <child2>another-test</child2>
</foo>

Что похоже на ваш вопрос.

Steven_W
источник
minOccursи maxOccursограничены до 1 для детей xs:all.
Павел Минаев
Павел: Спасибо ... Я выяснил это,
перепроверив
1

Если ничего из вышеперечисленного не работает, вы, вероятно, работаете над транзакцией EDI, где вам нужно проверить свой результат на соответствие схеме HIPPA или любому другому сложному xsd в этом отношении. Требование состоит в том, что, скажем, имеется 8 сегментов REF, и любой из них должен появляться в любом порядке, а также не все требуются, означает, что вы можете иметь их в следующем порядке: 1-й REF, 3-й REF, 2-й REF, 9-й REF. В ситуации по умолчанию получение EDI не удастся, поскольку сложный тип по умолчанию

<xs:sequence>
  <xs:element.../>
</xs:sequence>

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

<xs:element>
<xs:complexType>
<xs:sequence>
<element name="REF1"  ref= "REF1_Mycustomelment" minOccurs="0" maxOccurs="1">
<element name="REF2"  ref= "REF2_Mycustomelment" minOccurs="0" maxOccurs="1">
<element name="REF3"  ref= "REF3_Mycustomelment" minOccurs="0" maxOccurs="1">
</xs:sequence>
</xs:complexType>
</xs:element>

Решение:

Здесь просто заменить «последовательность» на «все» или использовать «выбор» с комбинациями мин / макс не сработает!

Первым делом замените. "xs:sequence" with "<xs:all>" Теперь вам нужно внести некоторые изменения в то место, откуда вы ссылаетесь на элемент, перейдите по ссылке:

<xs:annotation>
  <xs:appinfo>
    <b:recordinfo structure="delimited" field.........Biztalk/2003">

*** Теперь в приведенном выше сегменте добавьте в конце точку запуска, например, trigger_field = "REF01 _... полное имя .." trigger_value = "38" Сделайте то же самое для других сегментов REF, где значение триггера будет другим, например, "18 "," XX "," YY "и т. Д., Чтобы информация о вашей записи теперь выглядела так:b:recordinfo structure="delimited" field.........Biztalk/2003" trigger_field="REF01_...complete name.." trigger_value="38">


Это сделает каждый элемент уникальным, поскольку все сегменты REF (пример выше) имеют такую ​​же структуру, как REF01, REF02, REF03. И во время проверки проверка структуры в порядке, но она не позволяет значениям повторяться, потому что пытается найти оставшиеся значения в самом первом REF. Добавление триггеров сделает их все уникальными, и они будут проходить в любом порядке и ситуационных случаях (например, используйте 5 из 9, а не все 9/9).

Надеюсь, это поможет вам, потому что я потратил на это почти 20 часов.

Удачи

Прабхдип Гилл
источник