XSD - how to allow elements in any order any number of times?
Asked Answered
G

7

123

I am trying to create an XSD, and trying to write the definition with the following requirement:

  • Allow child element specified to appear any number of times (0 to unbounded)
  • Allow child elements to be in any order

I looked around and found various solutions like this:

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

But from what I understand xs:choice still only allows single element selection. Hence setting the MaxOccurs to unbounded like this should only mean that "any one" of the child elements can appear multiple times. Is this accurate?

If above solution is incorrect, how can I achieve what I stated above in my requirement?

EDIT: What if the requirement is as follows?

  • Element child1 child2 can appear any number of times (0 to unbounded)
  • Elements to be in any order
  • Elements child3 and child4 should appear exactly once.

For example, this xml is valid:

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

but this is not (missing child3)

<foo>
<child1> value </child1>
<child1> value </child1>
<child2> value </child2>
<child4> value </child4>
<child1> value </child1>
</foo>
Grindery answered 18/2, 2010 at 16:44 Comment(0)
R
70

In the schema you have in your question, child1 or child2 can appear in any order, any number of times. So this sounds like what you are looking for.

If you wanted only one of them to appear an unlimited number of times, the unbounded would have to go on the elements instead:

<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>
Rinse answered 18/2, 2010 at 17:14 Comment(5)
basically yes, i am looking for elements child1, child2 to appear in any order, any number of times.. the answer you provided here only works for single element, right? or does this solve my requirement also?Grindery
The schema in your question meets your requirement; the alternative schema in my answer is for a single element. Hope that clears it up! :)Rinse
@Pavel, @xcut, Thanks for clarification, see edited requirement.. any thoughts?Grindery
jvtech: you cannot satisfy that edited requirement with XML schema; the only way to achieve it would be if child3 and child4 can appear only at the end. In which case you need a sequence containing a choice and then the two elements.Rinse
@Daij-Djan I also found that it didn't work. Try adding maxOccurs="unbounded" on the choice element so that more than one child element is permitted.Finisterre
A
118

The alternative formulation of the question added in a later edit seems still to be unanswered: how to specify that among the children of an element, there must be one named child3, one named child4, and any number named child1 or child2, with no constraint on the order in which the children appear.

This is a straightforwardly definable regular language, and the content model you need is isomorphic to a regular expression defining the set of strings in which the digits '3' and '4' each occur exactly once, and the digits '1' and '2' occur any number of times. If it's not obvious how to write this, it may help to think about what kind of finite state machine you would build to recognize such a language. It would have at least four distinct states:

  • an initial state in which neither '3' nor '4' has been seen
  • an intermediate state in which '3' has been seen but not '4'
  • an intermediate state in which '4' has been seen but not '3'
  • a final state in which both '3' and '4' have been seen

No matter what state the automaton is in, '1' and '2' may be read; they do not change the machine's state. In the initial state, '3' or '4' will also be accepted; in the intermediate states, only '4' or '3' is accepted; in the final state, neither '3' nor '4' is accepted. The structure of the regular expression is easiest to understand if we first define a regex for the subset of our language in which only '3' and '4' occur:

(34)|(43)

To allow '1' or '2' to occur any number of times at a given location, we can insert (1|2)* (or [12]* if our regex language accepts that notation). Inserting this expression at all available locations, we get

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

Translating this into a content model is straightforward. The basic structure is equivalent to the 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>

Inserting a zero-or-more choice of child1 and child2 is straightforward:

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

If we want to minimize the bulk a bit, we can define a named group for the repeating choices of child1 and 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>

In XSD 1.1, some of the constraints on all-groups have been lifted, so it's possible to define this content model more concisely:

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

But as can be seen from the examples given earlier, these changes to all-groups do not in fact change the expressive power of the language; they only make the definition of certain kinds of languages more succinct.

Agar answered 17/8, 2012 at 20:13 Comment(9)
I like the XSD 1.0 xs:all alternative.Estriol
+1. This is an excellent answer and it deserves way more upvotes.Anarthrous
Great answer ! I really like explanations like this. It reveals all the logic and the reasoning behind the achievement of the goal. Now I do not only know how to solve this problem, but I learned a new approach of solving similar problems. Explaining this using a finite state automation is very good idea.Escalera
Michael, you say "these changes to all-groups do not in fact change the expressive power of the language; they only make the definition of certain kinds of languages more succinct". But if you generalize the problem to any number of children elements, a subset of which can appear once and another subset which can appear any number of times, the XSD 1.0 solution would yield to a combinatorial explosion, wouldn't it? While the XSD 1.1 solution would remain clean.Teamster
ebruchez, yes -- expressive power, as I use the term, is not the same as succinctness, compactness, terseness, or manageability. Expressive power only asks "Can this formalism define this language?" It does not ask about the size of the grammar, or about whether some syntactic sugar would make it smaller. The combinatorial explosion you mention means that handling large sets of elements without the XSD 1.1 changes to all-groups becomes very unpleasant very fast (and for large n may may exhaust memory). It does not mean that they become impossible in principle.Agar
Great write up, but where would the element declarations fall in this? You have to use references in the choice list to ensure uniqueness of the actual elements, but there's nowhere in this nested tree you've shown that you can put actual element declarations.Homelike
@mtalexan, the element declarations pointed to by the element references are (as always) top-level / global element declarations. They would be siblings to the top-level complex types whose declarations are shown. Or, if one prefers, the element references shown can all be replaced by local element declarations. That introduces redundancy and makes errors in maintenance more likely, but it is not in itself forbidden.Agar
@C.M.Sperberg-McQueen I didn't follow all the details, but does this mean XSD 1.0 can express “an arbitrary sequence of any combination of element A and element B but at least one of either”? Or did your example above tie down the ordering of the elements, as I think it does?Teahan
@Teahan Not sure I understand your question or the nature of your confusion. None of the four solutions in my answer constrains the ordering of child elements and I don't know what would suggest that they do. As for the XSD equivalent of (a|b)+, which matches an arbitrary sequence of a and b but at least one of either, it's not complicated. (Sorry for slow response.)Agar
R
70

In the schema you have in your question, child1 or child2 can appear in any order, any number of times. So this sounds like what you are looking for.

If you wanted only one of them to appear an unlimited number of times, the unbounded would have to go on the elements instead:

<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>
Rinse answered 18/2, 2010 at 17:14 Comment(5)
basically yes, i am looking for elements child1, child2 to appear in any order, any number of times.. the answer you provided here only works for single element, right? or does this solve my requirement also?Grindery
The schema in your question meets your requirement; the alternative schema in my answer is for a single element. Hope that clears it up! :)Rinse
@Pavel, @xcut, Thanks for clarification, see edited requirement.. any thoughts?Grindery
jvtech: you cannot satisfy that edited requirement with XML schema; the only way to achieve it would be if child3 and child4 can appear only at the end. In which case you need a sequence containing a choice and then the two elements.Rinse
@Daij-Djan I also found that it didn't work. Try adding maxOccurs="unbounded" on the choice element so that more than one child element is permitted.Finisterre
L
55

This is what finally worked for me:

<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>
Limoli answered 20/4, 2011 at 20:12 Comment(3)
Indeed the trick is to use xsd:choice with the quantifiers <xsd:choice minOccurs="0" maxOccurs="unbounded">Womanhater
I think it's worth pointing out that the above example works even without the sequence-element enclosing the choice-element.Stochastic
Does’t this one violate the op’s constraint that Elements child3 and child4 should appear exactly once?Androcles
C
10

But from what I understand xs:choice still only allows single element selection. Hence setting the MaxOccurs to unbounded like this should only mean that "any one" of the child elements can appear multiple times. Is this accurate?

No. The choice happens individually for every "repetition" of xs:choice that occurs due to maxOccurs="unbounded". Therefore, the code that you have posted is correct, and will actually do what you want as written.

Creatine answered 19/2, 2010 at 17:29 Comment(1)
Your comment with the answer provided by @Limoli explains it all nicely.Myer
B
4

You should find that the following schema allows the what you have proposed.

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

This will allow you to create a file such as:

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

Which seems to match your question.

Banc answered 19/2, 2010 at 17:26 Comment(2)
minOccurs and maxOccurs are restricted to 1 for children of xs:all.Creatine
Pavel: Thanks ... I found this out after double-checking my post and then edited it to remove the xs:allBanc
A
1

If none of the above is working, you are probably working on EDI trasaction where you need to validate your result against an HIPPA schema or any other complex xsd for that matter. The requirement is that, say there 8 REF segments and any of them have to appear in any order and also not all are required, means to say you may have them in following order 1st REF, 3rd REF , 2nd REF, 9th REF. Under default situation EDI receive will fail, beacause default complex type is

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

The situation is even complex when you are calling your element by refrence and then that element in its original spot is quite complex itself. for example:

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

Here simply replacing "sequence" with "all" or using "choice" with min/max combinations won't work!

First thing replace "xs:sequence" with "<xs:all>" Now,You need to make some changes where you are Referring the element from, There go to:

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

***Now in the above segment add trigger point in the end like this trigger_field="REF01_...complete name.." trigger_value = "38" Do the same for other REF segments where trigger value will be different like say "18", "XX" , "YY" etc..so that your record info now looks like:b:recordinfo structure="delimited" field.........Biztalk/2003" trigger_field="REF01_...complete name.." trigger_value="38">


This will make each element unique, reason being All REF segements (above example) have same structure like REF01, REF02, REF03. And during validation the structure validation is ok but it doesn't let the values repeat because it tries to look for remaining values in first REF itself. Adding triggers will make them all unique and they will pass in any order and situational cases (like use 5 out 9 and not all 9/9).

Hope it helps you, for I spent almost 20 hrs on this.

Good Luck

Aborigine answered 15/5, 2013 at 11:40 Comment(0)
S
0

If you have very few child elements, then you could list all possible sequences inside a <xs:choice>. That gives per sequence the flexibility you want. As N! is growing very rapidly, this only suits up to ~4 child elements.

Sn answered 30/6, 2022 at 11:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.