25
votes

I'm trying to set up part of a schema that's like a "Sequence" where all child elements are optional, but at least one of the elements must be present, and there could be more than one of them.

I tried doing the following, but XMLSpy complains that "The content model contains the elements <element name="DateConstant"> and <element name="DateConstant"> which cannot be uniquely determined.":

    <xs:choice>
        <xs:sequence>
            <xs:element name="DateConstant"/>
            <xs:element name="TimeConstant"/>
        </xs:sequence>
        <xs:element name="DateConstant"/>
        <xs:element name="TimeConstant"/>
    </xs:choice>

Can this be done (and if so, how)?

Some clarification: I only want to allow one of each element of the same name. There can be one "DateConstant" and/or one "TimeConstant", but not two of either. Gizmo's answer matches my requirements, but it's impractical for a larger number of elements. Hurst's answer allows two or more elements of the same name, which I don't want.

3

3 Answers

26
votes

Try this:

<xs:choice>
  <xs:sequence>
    <xs:element name="Elem1" />
    <xs:element name="Elem2" minOccurs="0" />
    <xs:element name="Elem3" minOccurs="0" />
  </xs:sequence>
  <xs:sequence>
    <xs:element name="Elem2" />
    <xs:element name="Elem3" minOccurs="0" />
  </xs:sequence>
  <xs:element name="Elem3" />
</xs:choice>

Doing so, you force either to choose the first element and then the rest is optional, either the second element and the rest is optional, either the third element.

This should do what you want, I hope.

Of course, you could place the sub-sequences into groups, to avoid to duplicate an element in each sequence if you realize you miss one.

19
votes

According to the technical article on MSDN titled Understanding XML Schema at http://msdn.microsoft.com/en-us/library/aa468557.aspx#understandxsd_topic5 you can take advantage of constraints such as minOccurs on the choice definition (compositor) itself:

"Using occurrence constraints on a compositor applies to the entire group as a whole"

(See the more sophisticated example that uses nested complex types and the AuthorType example)

You stated your requirement as "at least one of the elements must be present, and there could be more than one of them". Thus, I propose you try the following:

<xs:choice minOccurs="1" maxOccurs="unbounded">
    <xs:element name="DateConstant" type="..."/>
    <xs:element name="TimeConstant" type="..."/>
</xs:choice>
2
votes

@hurst,

Unfortunately you have failed to understand the original question. Placing minOccurs="1" on the choice is satisfied automatically when ALL elements that have minOccurs="0" are contained as options.

Thus you have failed to account for the "at least one" required by the original poster, because no elements correctly satisfies 1 occurrance of two completely optional elements.

So far I am unable to find a solution to this as minOccur/maxOccur both relate to the group in which they are defined and DO NOT relate to an overall number of nodes. Nor can you use the choice element to define the same named element more than once or it becomes "ambiguous". I have seen some examples use references instead of elements of a specific type, but I believe this fails the microsoft XSD parser.

<xs:choice minOccurs="1" maxOccurs="1">
  <xs:sequence minOccurs="1" maxOccurs="1">
    <xs:element name="Elem1" minOccurs="1" maxOccurs="1" />
    <xs:element name="Elem2" minOccurs="0" maxOccurs="1" />
  </xs:sequence>
  <xs:sequence >
    <xs:element name="Elem2" minOccurs="1" maxOccurs="1" />
  </xs:sequence>
</xs:choice>

Here you can see that either you have the first sequence (which MUST have Elem1 but may have Elem2 optionally), or you have the second sequence (which MUST have Elem2).

Hence you now have "any one or more" of these 2 elements. Of course this gets exponentially more complex the more options you have as you need to provide additional choices for all possible combinations.