121 votes

XSD - comment autoriser les éléments dans n'importe quel ordre un nombre illimité de fois ?

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

  • Permet à l'élément enfant spécifié d'apparaître un nombre quelconque de fois (0 à illimité).
  • Permettre aux éléments enfants d'être dans n'importe quel ordre

J'ai cherché et trouvé diverses solutions comme ce :

<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:choice ne permet toujours que la sélection d'un seul élément. Par conséquent, définir MaxOccurs sur unbounded comme ceci devrait signifier 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 child1 child2 peut apparaître un nombre quelconque nombre de fois (de 0 à illimité).
  • Les éléments peuvent ê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 le cas (enfant manquant3)

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

118voto

La formulation alternative de la question ajoutée dans une édition ultérieure semble toujours 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 tout nombre nommé child1 ou child2 sans aucune contrainte quant à l'ordre dans lequel les enfants apparaissent.

Il s'agit d'un langage régulier directement définissable, et le modèle de contenu dont vous avez besoin est isomorphe à une expression régulière définissant l'ensemble des chaînes de caractères dans lesquelles les chiffres "3" et "4" apparaissent chacun exactement une fois, et les chiffres "1" et "2" apparaissent un nombre quelconque de fois. Si la façon d'écrire ceci 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. Elle 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 le "4" a été vu mais pas le "3".
  • un état final dans lequel à la fois "3" et "4" ont été vus.

Quel que soit l'état de l'automate, '1' et '2' peuvent être lus ; ils ne changent pas l'état de la machine. Dans l'état initial, '3' ou '4' seront également acceptés ; 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 plus facile à comprendre si nous définissons d'abord une regex pour le sous-ensemble de notre langue dans lequel seuls '3' et '4' apparaissent :

(34)|(43)

Pour permettre à '1' ou '2' de se produire un nombre quelconque 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 endroits disponibles, on obtient

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

La transposition de ces données dans un modèle de contenu est simple. La structure de base est équivalente à la regex (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>

Insertion d'un choix de zéro ou plus de child1 et child2 est 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 l'encombrement, nous pouvons définir un groupe nommé pour les choix répétés de child1 et 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 la norme XSD 1.1, certaines des contraintes sur les fichiers all -Les groupes ont été supprimés, de sorte qu'il est 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 le montrent les exemples donnés précédemment, ces changements de all -En fait, les groupes ne modifient pas le pouvoir expressif de la langue ; ils ne font que rendre plus succincte la définition de certains types de langues.

3 votes

J'aime l'alternative XSD 1.0 xs:all.

8 votes

+1. C'est une excellente réponse et elle mérite bien plus de votes positifs.

1 votes

Excellente réponse ! J'aime beaucoup les explications comme celle-ci. Elles révèlent toute la logique et le raisonnement qui sous-tendent 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.

69voto

xcut Points 3766

Dans le schéma que vous avez dans votre question, child1 ou child2 peuvent apparaître dans n'importe quel ordre, un nombre illimité 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, il faudrait que le non limité aille sur les éléments à la place :

Edit : Type fixe en XML.

Edit : O capitalisé 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>

0 votes

En fait, oui, je cherche à ce que les éléments child1, child2 apparaissent 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, n'est-ce pas ? ou est-ce que cela résout aussi mon problème ?

0 votes

Le schéma présenté dans votre question répond à votre besoin ; le schéma alternatif présenté dans ma réponse concerne un seul élément. J'espère que tout cela est clair ! :)

0 votes

@Pavel, @xcut, Merci pour la clarification, voir l'exigence éditée des idées ?

54voto

Alan Points 331

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>

6 votes

En effet, l'astuce consiste à utiliser xsd:choice avec les quantificateurs <xsd:choice minOccurs="0" maxOccurs="unbounded">.

8 votes

Je pense qu'il est utile de souligner que l'exemple ci-dessus fonctionne même sans l'élément de séquence entourant l'élément de choix.

2 votes

Est-ce que celui-ci ne viole pas la contrainte de l'opération qui veut que Elements child3 and child4 should appear exactly once ?

10voto

Pavel Minaev Points 60647

Mais d'après ce que je comprends, xs:choice ne permet toujours que la sélection d'un seul élément. Par conséquent, définir MaxOccurs sur unbounded comme ceci devrait signifier que "n'importe lequel" des éléments enfants peut apparaître plusieurs fois. Est-ce exact ?

Non. Le choix s'effectue individuellement pour chaque "répétition" de xs:choice qui se produit en raison de maxOccurs="unbounded" . Par conséquent, le code que vous avez affiché est correct, et fera ce que vous voulez tel qu'il est écrit.

0 votes

Votre commentaire avec la réponse fournie par @Alan explique bien tout cela.

4voto

Steven_W Points 195

Vous devriez constater que le schéma suivant permet 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.

0 votes

minOccurs et maxOccurs sont limitées à 1 pour les enfants de xs:all .

0 votes

Pavel : Merci ... J'ai découvert cela après avoir vérifié deux fois mon message et je l'ai modifié pour enlever le xs:all.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X