XSD - jak dowolną liczbę razy dopuścić elementy w dowolnej kolejności?

109

Próbuję utworzyć XSD i próbuję napisać definicję z następującym wymaganiem:

  • Zezwalaj, aby określony element podrzędny pojawiał się dowolną liczbę razy (od 0 do nieograniczonych)
  • Pozwól elementom podrzędnym być w dowolnej kolejności

Rozejrzałem się i znalazłem różne rozwiązania, takie jak to :

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

Ale z tego, co rozumiem, xs: wybór nadal pozwala na wybór tylko jednego elementu. Stąd ustawienie MaxOccurs na nieograniczony w ten sposób powinno tylko oznaczać, że „dowolny” z elementów podrzędnych może pojawić się wiele razy. Czy to jest dokładne?

Jeśli powyższe rozwiązanie jest nieprawidłowe, jak mogę osiągnąć to, co wskazałem powyżej w moim wymaganiu?

EDYCJA : A co, jeśli wymagania są następujące?

  • Element child1 child2 może pojawić się dowolną liczbę razy (od 0 do nieograniczonego)
  • Elementy w dowolnej kolejności
  • Elementy child3 i child4 powinny pojawić się dokładnie raz.

Na przykład ten xml jest prawidłowy:

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

ale to nie jest (brakujące dziecko3)

<foo>
<child1> value </child1>
<child1> value </child1>
<child2> value </child2>
<child4> value </child4>
<child1> value </child1>
</foo>
jvtech
źródło

Odpowiedzi:

62

W schemacie, który masz w swoim pytaniu, child1lub child2może pojawić się w dowolnej kolejności, dowolną liczbę razy. Więc to brzmi jak to, czego szukasz.

Edycja: jeśli chcesz, aby tylko jeden z nich pojawił się nieograniczoną liczbę razy, nieograniczony musiałby zamiast tego przejść do elementów:

Edycja: Naprawiono typ w XML.

Edycja: pisane wielką literą O w 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
źródło
w zasadzie tak, szukam elementów child1, child2, aby pojawiały się w dowolnej kolejności, dowolną liczbę razy ... odpowiedź, którą tu podałeś, działa tylko dla pojedynczego elementu, prawda? czy to również rozwiązuje moje wymagania?
jvtech
Schemat w pytaniu spełnia Twoje wymagania; alternatywny schemat w mojej odpowiedzi dotyczy pojedynczego elementu. Mam nadzieję, że to wyjaśnia sprawę! :)
xcut
@Pavel, @xcut, Dzięki za wyjaśnienie, zobacz zredagowane wymaganie .. jakieś przemyślenia?
jvtech
2
jvtech: nie można spełnić tego edytowanego wymagania za pomocą schematu XML; jedynym sposobem na osiągnięcie tego byłoby, gdyby child3 i child4 mogły pojawić się dopiero na końcu. W takim przypadku potrzebujesz sekwencji zawierającej wybór, a następnie dwa elementy.
xcut
1
@ Daij-Djan Odkryłem również, że to nie działa. Spróbuj dodać maxOccurs = "unbounded" na elemencie choice, aby więcej niż jeden element podrzędny był dozwolony.
MikeD
107

Alternatywne sformułowanie pytania dodanego w późniejszej edycji wydaje się nadal pozostawać bez odpowiedzi: jak sprecyzować, że wśród elementów potomnych elementu musi być jeden nazwany child3, jeden nazwany child4i jakakolwiek nazwana liczba child1lub child2, bez ograniczenia kolejności w które pojawiają się dzieci.

Jest to łatwo definiowalny język regularny, a model treści, którego potrzebujesz, jest izomorficzny z wyrażeniem regularnym definiującym zestaw ciągów, w których cyfry „3” i „4” występują dokładnie raz, a cyfry „1” i „2” 'występują dowolną liczbę razy. Jeśli nie jest oczywiste, jak to napisać, warto pomyśleć o rodzaju skończonej maszyny stanowej, którą można by zbudować, aby rozpoznawać taki język. Miałoby co najmniej cztery różne stany:

  • stan początkowy, w którym nie zaobserwowano ani „3”, ani „4”
  • stan pośredni, w którym widać „3”, ale nie „4”
  • stan pośredni, w którym widać „4”, ale nie „3”
  • stan końcowy, w którym widać zarówno „3”, jak i „4”

Bez względu na stan automatu można odczytać „1” i „2”; nie zmieniają stanu maszyny. W stanie początkowym akceptowana będzie również wartość „3” lub „4”; w stanach pośrednich dopuszcza się tylko „4” lub „3”; w ostatecznym stanie ani „3”, ani „4” nie są akceptowane. Struktura wyrażenia regularnego jest najłatwiejsza do zrozumienia, jeśli najpierw zdefiniujemy wyrażenie regularne dla podzbioru naszego języka, w którym występuje tylko „3” i „4”:

(34)|(43)

Aby umożliwić wystąpienie „1” lub „2” dowolną liczbę razy w danym miejscu, możemy wstawić (1|2)*(lub [12]*jeśli nasz język regex akceptuje tę notację). Wstawiając to wyrażenie we wszystkich dostępnych lokalizacjach, otrzymujemy

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

Przełożenie tego na model treści jest proste. Podstawowa struktura jest odpowiednikiem wyrażenia regularnego (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>

Wstawienie opcji równej zero lub więcej child1i child2jest proste:

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

Jeśli chcemy nieco zminimalizować masę, możemy zdefiniować nazwaną grupę dla powtarzających się wyborów child1i 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>

W XSD 1.1 niektóre ograniczenia na all-groups zostały zniesione, więc możliwe jest bardziej zwięzłe zdefiniowanie tego modelu zawartości:

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

Ale jak widać na podanych wcześniej przykładach, te zmiany w all-grupach w rzeczywistości nie zmieniają ekspresyjnej siły języka; uściślają jedynie definicje niektórych rodzajów języków.

CM Sperberg-McQueen
źródło
3
Podoba mi się XSD 1.0 xs: wszystkie alternatywy.
TWiStErRob
8
+1. To doskonała odpowiedź i zasługuje na dużo więcej głosów.
Christoffer Lette
1
Świetna odpowiedź ! Naprawdę lubię takie wyjaśnienia. Ujawnia całą logikę i rozumowanie stojące za osiągnięciem celu. Teraz nie tylko wiem, jak rozwiązać ten problem, ale nauczyłem się nowego podejścia do rozwiązywania podobnych problemów. Wyjaśnienie tego za pomocą automatyzacji skończonej jest bardzo dobrym pomysłem.
egelev
3
Michael, mówisz, że „te zmiany dotyczące wszystkich grup w rzeczywistości nie zmieniają ekspresyjnej mocy języka, a jedynie sprawiają, że definicje niektórych rodzajów języków są bardziej zwięzłe”. Ale jeśli uogólnisz problem na dowolną liczbę elementów potomnych, z których podzbiór może pojawić się raz i inny podzbiór, który może pojawić się dowolną liczbę razy, rozwiązanie XSD 1.0 doprowadziłoby do eksplozji kombinatorycznej, prawda? Chociaż rozwiązanie XSD 1.1 pozostanie czyste.
ebruchez
1
ebruchez, tak - siłę wyrazu , jak używam tego terminu, nie jest taka sama jak zwięzłość , zwartość , lapidarnością, jakiej lub zarządzania . Siła ekspresji pyta tylko: „Czy ten formalizm może zdefiniować ten język?” Nie pyta o rozmiar gramatyki ani o to, czy jakiś cukier syntaktyczny mógłby ją zmniejszyć. Wspomniana eksplozja kombinatoryczna oznacza, że ​​obsługa dużych zestawów elementów bez zmian XSD 1.1 we wszystkich grupach staje się bardzo nieprzyjemna, bardzo szybko (a dla dużych n może wyczerpać pamięć). Nie oznacza to, że w zasadzie stają się one niemożliwe.
CM Sperberg-McQueen
49

Oto, co w końcu zadziałało:

<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>
Alan
źródło
5
Rzeczywiście, sztuczka polega na użyciu xsd: choice z kwantyfikatorami <xsd: choice minOccurs = "0" maxOccurs = "unbounded">
tivo
6
Myślę, że warto zwrócić uwagę, że powyższy przykład działa nawet bez elementu sekwencji otaczającego element choice.
9

Ale z tego, co rozumiem, xs: wybór nadal pozwala na wybór tylko jednego elementu. Stąd ustawienie MaxOccurs na nieograniczony w ten sposób powinno tylko oznaczać, że „dowolny” z elementów podrzędnych może pojawić się wiele razy. Czy to jest dokładne?

Nie. Wybór następuje indywidualnie dla każdego „powtórzenia” tego, xs:choiceco następuje z powodu maxOccurs="unbounded". Dlatego kod, który opublikowałeś, jest poprawny i faktycznie będzie robił, co chcesz, tak jak napisałeś.

Pavel Minaev
źródło
Twój komentarz wraz z odpowiedzią udzieloną przez @Alan ładnie to wszystko wyjaśnia.
urodzony
3

Powinieneś zauważyć, że następujący schemat pozwala na to, co zaproponowałeś.

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

Umożliwi to utworzenie takiego pliku, jak:

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

Co wydaje się pasować do twojego pytania.

Steven_W
źródło
minOccursi maxOccurssą ograniczone do 1 dla dzieci w wieku xs:all.
Pavel Minaev
Pavel: Dzięki ... Dowiedziałem się o tym po dwukrotnym sprawdzeniu mojego posta, a następnie zredagowałem go, aby usunąć xs: all
Steven_W
1

Jeśli żadne z powyższych nie działa, prawdopodobnie pracujesz nad transakcją EDI, w której musisz zweryfikować swój wynik ze schematem HIPPA lub innym złożonym xsd w tym zakresie. Wymóg jest taki, że powiedzmy, że istnieje 8 segmentów REF i każdy z nich musi pojawić się w dowolnej kolejności, a także nie wszystkie są wymagane, co oznacza, że ​​możesz mieć je w następującej kolejności: 1 REF, 3 REF, 2 REF, 9 REF. W sytuacji domyślnej odbieranie EDI nie powiedzie się, ponieważ domyślnym typem złożonym jest

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

Sytuacja jest nawet złożona, gdy wywołujesz swój element przez odwołanie, a wtedy ten element w swoim pierwotnym miejscu jest sam w sobie dość złożony. na przykład:

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

Rozwiązanie:

Tutaj zwykłe zastąpienie „sekwencji” słowem „wszystko” lub użycie „wyboru” kombinacjami min / maks nie zadziała!

Najpierw zastąp "xs:sequence" with "<xs:all>" Teraz, musisz dokonać pewnych zmian w miejscu, z którego odwołujesz się do elementu, przejdź do:

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

*** Teraz w powyższym segmencie dodaj punkt wyzwalania na końcu, jak ten trigger_field = "REF01 _... pełna nazwa .." trigger_value = "38" Zrób to samo dla innych segmentów REF, w których wartość wyzwalania będzie inna, jak powiedzmy "18 ”,„ XX ”,„ YY ”itd., Aby informacje o Twoim rekordzie wyglądały teraz następująco:b:recordinfo structure="delimited" field.........Biztalk/2003" trigger_field="REF01_...complete name.." trigger_value="38">


Dzięki temu każdy element będzie wyjątkowy, ponieważ wszystkie segmenty REF (powyższy przykład) mają taką samą strukturę jak REF01, REF02, REF03. Podczas walidacji walidacja struktury jest w porządku, ale nie pozwala na powtórzenie wartości, ponieważ próbuje szukać pozostałych wartości w samym REF. Dodanie wyzwalaczy sprawi, że wszystkie będą unikalne i przejdą w dowolnej kolejności i sytuacjach sytuacyjnych (np. Użyj 5 z 9, a nie wszystkich 9/9).

Mam nadzieję, że ci to pomoże, bo spędziłem nad tym prawie 20 godzin.

Powodzenia

Prabhdeep Gill
źródło