XSD - comment autoriser des éléments dans n'importe quel ordre autant de fois que nécessaire?

109

J'essaie de créer un XSD et j'essaie d'écrire la définition avec l'exigence suivante:

  • Autoriser l'élément enfant spécifié à apparaître un nombre illimité de fois (de 0 à illimité)
  • Autoriser les éléments enfants à être dans n'importe quel ordre

J'ai regardé autour de moi et j'ai trouvé diverses solutions comme celle-ci :

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

Mais d'après ce que je comprends xs: le choix ne permet toujours que la sélection d'un seul élément. Par conséquent, si vous définissez MaxOccurs sur illimité comme ceci, cela signifie uniquement que "n'importe lequel" des éléments enfants peut apparaître plusieurs fois. Est-ce exact?

Si la solution ci-dessus est incorrecte, comment puis-je réaliser ce que j'ai indiqué ci-dessus dans mon exigence?

EDIT : Et si l'exigence est la suivante?

  • L'élément enfant1 enfant2 peut apparaître un nombre illimité de fois (de 0 à illimité)
  • Les éléments doivent être dans n'importe quel ordre
  • Les éléments child3 et child4 doivent apparaître exactement une fois.

Par exemple, ce xml est valide:

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

mais ce n'est pas (enfant manquant3)

<foo>
<child1> value </child1>
<child1> value </child1>
<child2> value </child2>
<child4> value </child4>
<child1> value </child1>
</foo>
jvtech
la source

Réponses:

61

Dans le schéma que vous avez dans votre question, child1ou child2peut apparaître dans n'importe quel ordre, n'importe quel nombre de fois. Cela ressemble donc à ce que vous recherchez.

Edit: si vous vouliez qu'un seul d'entre eux apparaisse un nombre illimité de fois, l'infini devrait aller sur les éléments à la place:

Edit: Type fixe dans XML.

Edit: O majuscule dans 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
la source
fondamentalement oui, je recherche les éléments child1, child2 pour apparaître dans n'importe quel ordre, n'importe quel nombre de fois .. la réponse que vous avez fournie ici ne fonctionne que pour un seul élément, non? ou est-ce que cela résout également mon besoin?
jvtech
Le schéma de votre question répond à vos besoins; le schéma alternatif dans ma réponse est pour un seul élément. J'espère que cela clarifie les choses! :)
xcut
@Pavel, @xcut, Merci pour la clarification, voir l'exigence modifiée .. des pensées?
jvtech
2
jvtech: vous ne pouvez pas satisfaire cette exigence modifiée avec un schéma XML; le seul moyen d'y parvenir serait que child3 et child4 ne puissent apparaître qu'à la fin. Dans ce cas, vous avez besoin d'une séquence contenant un choix puis les deux éléments.
xcut
1
@ Daij-Djan J'ai également constaté que cela ne fonctionnait pas. Essayez d'ajouter maxOccurs = "unbounded" sur l'élément de choix afin que plusieurs éléments enfants soient autorisés.
MikeD
107

La formulation alternative de la question ajoutée dans une édition ultérieure semble toujours être sans réponse: comment spécifier que parmi les enfants d'un élément, il doit y en avoir un nommé child3 , un nommé child4, et n'importe quel nombre nommé child1ou child2, sans contrainte sur l'ordre dans que les enfants apparaissent.

Il s'agit d'un langage régulier clairement définissable, et le modèle de contenu dont vous avez besoin est isomorphe à une expression régulière définissant l'ensemble de chaînes dans lequel les chiffres '3' et '4' apparaissent chacun exactement une fois, et les chiffres '1' et '2 'se produisent un certain nombre de fois. Si la manière d'écrire cela n'est pas évidente, il peut être utile de réfléchir au type de machine à états finis que vous construiriez pour reconnaître un tel langage. Il aurait au moins quatre états distincts:

  • un état initial dans lequel ni '3' ni '4' n'ont été vus
  • un état intermédiaire dans lequel '3' a été vu mais pas '4'
  • un état intermédiaire dans lequel '4' a été vu mais pas '3'
  • un état final dans lequel les deux '3' et '4' ont été vus

Quel que soit l'état dans lequel se trouve l'automate, «1» et «2» peuvent être lus; ils ne modifient pas l'état de la machine. Dans l'état initial, «3» ou «4» sera également accepté; dans les états intermédiaires, seuls «4» ou «3» sont acceptés; dans l'état final, ni «3» ni «4» ne sont acceptés. La structure de l'expression régulière est la plus facile à comprendre si nous définissons d'abord une expression régulière pour le sous-ensemble de notre langage dans lequel seuls '3' et '4' apparaissent:

(34)|(43)

Pour permettre à '1' ou '2' de se produire un nombre illimité de fois à un endroit donné, nous pouvons insérer (1|2)*(ou [12]*si notre langage regex accepte cette notation). En insérant cette expression à tous les emplacements disponibles, nous obtenons

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

Traduire cela en un modèle de contenu est simple. La structure de base est équivalente à l'expression régulière (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>

Insérer un choix zéro ou plus de child1et child2est simple:

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

Si nous voulons minimiser un peu le volume, nous pouvons définir un groupe nommé pour les choix répétés de child1et 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>

Dans XSD 1.1, certaines des contraintes sur les allgroupes ont été levées, il est donc possible de définir ce modèle de contenu de manière plus concise:

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

Mais comme on peut le voir d'après les exemples donnés précédemment, ces changements de allgroupes ne changent pas en fait le pouvoir expressif de la langue; ils ne font que rendre la définition de certains types de langues plus succincte.

CM Sperberg-McQueen
la source
3
J'aime le XSD 1.0 xs: toutes les alternatives.
TWiStErRob
8
+1. C'est une excellente réponse et elle mérite bien plus de votes positifs.
Christoffer Lette
1
Très bonne réponse ! J'aime beaucoup les explications comme ça. Il révèle toute la logique et le raisonnement derrière la réalisation de l'objectif. Maintenant, je ne sais pas seulement comment résoudre ce problème, mais j'ai appris une nouvelle approche pour résoudre des problèmes similaires. Expliquer cela en utilisant une automatisation à états finis est une très bonne idée.
egelev
3
Michael, vous dites "ces changements à tous les groupes ne changent pas en fait le pouvoir expressif de la langue; ils ne font que rendre la définition de certains types de langues plus succincte". Mais si vous généralisiez le problème à n'importe quel nombre d'éléments enfants, dont un sous-ensemble peut apparaître une fois et un autre sous-ensemble qui peut apparaître un nombre illimité de fois, la solution XSD 1.0 céderait à une explosion combinatoire, n'est-ce pas? Alors que la solution XSD 1.1 resterait propre.
ebruchez
1
ebruchez, oui - puissance expressive , comme je l' utilise le terme, ne sont pas les mêmes que succinctness , compacité , laconisme ou de gestion . Le pouvoir expressif demande seulement "Ce formalisme peut-il définir ce langage?" Il ne pose pas de question sur la taille de la grammaire, ni sur le fait que du sucre syntaxique la réduirait. L'explosion combinatoire que vous mentionnez signifie que la gestion de grands ensembles d'éléments sans les changements XSD 1.1 à tous les groupes devient très désagréable très rapidement (et pour un grand n peut épuiser la mémoire). Cela ne veut pas dire qu'ils deviennent impossibles en principe.
CM Sperberg-McQueen
49

C'est ce qui a finalement fonctionné pour moi:

<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
la source
5
En effet l'astuce consiste à utiliser xsd: choice avec les quantificateurs <xsd: choice minOccurs = "0" maxOccurs = "unbounded">
tivo
6
Je pense qu'il vaut la peine de souligner que l'exemple ci-dessus fonctionne même sans l'élément de séquence englobant l'élément de choix.
9

Mais d'après ce que je comprends xs: le choix ne permet toujours que la sélection d'un seul élément. Par conséquent, si vous définissez MaxOccurs sur illimité comme ceci, cela signifie uniquement que "n'importe lequel" des éléments enfants peut apparaître plusieurs fois. Est-ce exact?

Non. Le choix se produit individuellement pour chaque «répétition» de ce xs:choicequi se produit maxOccurs="unbounded". Par conséquent, le code que vous avez publié est correct et fera réellement ce que vous voulez tel qu'il est écrit.

Pavel Minaev
la source
Votre commentaire avec la réponse fournie par @Alan explique tout bien.
bor
3

Vous devriez constater que le schéma suivant autorise ce que vous avez proposé.

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

Cela vous permettra de créer un fichier tel que:

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

Ce qui semble correspondre à votre question.

Steven_W
la source
minOccurset maxOccurssont limités à 1 pour les enfants de xs:all.
Pavel Minaev
Pavel: Merci ... J'ai découvert cela après avoir revérifié mon message, puis je l'ai modifié pour supprimer le xs: all
Steven_W
1

Si rien de ce qui précède ne fonctionne, vous travaillez probablement sur une transaction EDI où vous devez valider votre résultat par rapport à un schéma HIPPA ou à tout autre xsd complexe d'ailleurs. La condition est que, disons qu'il y a 8 segments REF et que l'un d'eux doit apparaître dans n'importe quel ordre et que tous ne sont pas nécessaires, cela signifie que vous pouvez les avoir dans l'ordre suivant 1er REF, 3e REF, 2e REF, 9e REF. Dans la situation par défaut, la réception EDI échouera, car le type complexe par défaut est

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

La situation est même complexe lorsque vous appelez votre élément par réfraction et que cet élément à son emplacement d'origine est lui-même assez complexe. par exemple:

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

Solution:

Ici, simplement remplacer «séquence» par «tous» ou utiliser «choix» par des combinaisons min / max ne fonctionnera pas!

Première chose à remplacer "xs:sequence" with "<xs:all>" Maintenant, vous devez apporter des modifications à l'endroit d'où vous faites référence à l'élément, allez à:

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

*** Maintenant, dans le segment ci-dessus, ajoutez un point de déclenchement à la fin comme ceci trigger_field = "REF01 _... nom complet .." trigger_value = "38" Faites de même pour les autres segments REF où la valeur de déclenchement sera différente comme par exemple "18 "," XX "," YY "etc. pour que les informations de votre enregistrement ressemblent maintenant à:b:recordinfo structure="delimited" field.........Biztalk/2003" trigger_field="REF01_...complete name.." trigger_value="38">


Cela rendra chaque élément unique, la raison étant que tous les segments REF (exemple ci-dessus) ont la même structure que REF01, REF02, REF03. Et pendant la validation, la validation de la structure est correcte mais elle ne laisse pas les valeurs se répéter car elle essaie de rechercher les valeurs restantes dans le premier REF lui-même. L'ajout de déclencheurs les rendra tous uniques et ils passeront dans n'importe quel ordre et dans n'importe quel cas de situation (comme utiliser 5 sur 9 et pas tous 9/9).

J'espère que cela vous aidera, car j'ai passé près de 20 heures là-dessus.

Bonne chance

Prabhdeep Gill
la source