XSD: specify a choice must have at least one from a list but no more than one of each

xml-validationxsd

This has been driving me mad for hours. I've read every relevant XSD question on SO and the rest of the Internet it seems and still the answer eludes me.

I need an XML schema that requires at least one of list of elements be present, but each element may appear only 0 or 1 times.

This is similar to this question:
XML schema construct for "any one or more of these elements but must be at least one"

but I was unable to constrain the upper limit: I am apparently using maxOccursincorrectly.

Here's where I left off with my schema:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:complexType name="Selects">
        <xs:sequence minOccurs="2" maxOccurs="4">
        <xs:choice>
        <xs:element name="aaa" minOccurs="1" maxOccurs="1"/>
        <xs:element name="bbb" minOccurs="1" maxOccurs="1"/>
        <xs:element name="ccc" minOccurs="1" maxOccurs="1"/>
        <xs:element name="ddd" minOccurs="1" maxOccurs="1"/>
        </xs:choice>
    </xs:sequence>
    </xs:complexType>
    <xs:element name="baseElement">
        <xs:complexType>
        <xs:sequence>
        <xs:element name="MyChoice" type="Selects"/>
            </xs:sequence>
    </xs:complexType>
    </xs:element>
</xs:schema>

I've tried the minOccurs and maxOccurs on the choice and the element with no luck. Here is XML that validates, though I don't want it to:

<?xml version="1.0" encoding="UTF-8"?>
<baseElement xsi:noNamespaceSchemaLocation="myTest.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <MyChoice>
        <ddd/>
        <ddd/>
    </MyChoice>
</baseElement>

Here's an example of what I would like, if possible:

<?xml version="1.0" encoding="UTF-8"?>
<baseElement xsi:noNamespaceSchemaLocation="myTest.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <MyChoice>
        <ddd/>
        <aaa/>
        <ccc/>
    </MyChoice>
</baseElement>

I would like it to complain about the multiple ddd elements but allow any or all of the others in any order. I get an error if I only have one element under MyChoice so at least something works.

What am I doing wrong? How do I prevent multiple of the same element from validating?

UPDATE

This was my solution (from comments on answer below):

Actually, xs:all did the trick. I swapped the choice for all and added minOccurs="0" maxOccurs="1" to each element. With xs:all, minOccurs must be either 0 or 1 and maxOccurs must be 1. Thanks for your help – I'm good to go now!

Best Answer

Just move the <xs:sequence minOccurs="2" maxOccurs="4"> from around the choice to the point where you want to use it further down. (you can also remove the min/max occurs = 1 as this is what xs:choice does)

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
  <xs:complexType name="Selects">
    <xs:choice>
      <xs:element name="aaa" />
      <xs:element name="bbb" />
      <xs:element name="ccc" />
      <xs:element name="ddd" />
    </xs:choice>
  </xs:complexType>
  <xs:element name="baseElement">
    <xs:complexType>
      <xs:sequence minOccurs="2" maxOccurs="4">
        <xs:element name="MyChoice" type="Selects" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

This validates the following:

<baseElement xsi:noNamespaceSchemaLocation="myTest.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <MyChoice>
    <bbb></bbb>
  </MyChoice>
  <MyChoice>
    <ccc></ccc>
  </MyChoice>
</baseElement>

UPDATE

I think you are reaching the limit of what you can achieve with XSD. I can't see any way you can do this other than by defining a "version" of the MyChoice type for every single combination possible (which will then need different names MyChoice1, MyChoice2 etc)

You can also use xs:all

  <xs:complexType name="Selects">
    <xs:all minOccurs=2 maxOccurs=4>
      <xs:element name="aaa" />
      <xs:element name="bbb" />
      <xs:element name="ccc" />
      <xs:element name="ddd" />
    </xs:all>
  </xs:complexType>

but this will not prevent you from having four <ddd/>'s

Related Topic