24

I have a choice complexType named abType:

<xs:complexType name="abType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="a"/>
        <xs:element name="b"/>
    </xs:choice>
</xs:complexType>

This type can be used to create elements with a and b nodes in any order like this for example:

<ab>
    <b/>
    <a/>
</ab>

Now I want to create a derived type called abcType to allow the nodes a, b and c in any order. Therefore I created a new complexType based on abType:

<xs:complexType name="abcType">
    <xs:complexContent>
        <xs:extension base="abType">
            <xs:choice minOccurs="0" maxOccurs="unbounded">
                <xs:element name="c"/>
            </xs:choice>
        </xs:extension>
    </xs:complexContent>
</xs:complexType>

After that I created a abc node:

<abc>
    <c/>
    <b/>
    <a/>
</abc>

But this node is invalid! It is not valid to put any a or b after a c. The reason is, that deriving a type from a base type creates an implicite sequence although both types are choices. XMLspy illustrates it in this way:

Extended Choice Type

This result is quite useless for choice types.

So my question is: How to extend a choice type without sequencing the choice?

Here is the complete XSD and an XML test file to reproduce the problem:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="root">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="ab"/>
            <xs:element ref="abc"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
<xs:element name="ab" type="abType"/>
<xs:complexType name="abType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="a"/>
        <xs:element name="b"/>
    </xs:choice>
</xs:complexType>
<xs:element name="abc" type="abcType"/>
<xs:complexType name="abcType">
    <xs:complexContent>
        <xs:extension base="abType">
            <xs:choice minOccurs="0" maxOccurs="unbounded">
                <xs:element name="c"/>
            </xs:choice>
        </xs:extension>
    </xs:complexContent>
</xs:complexType>
</xs:schema>

Example:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="inherit-choice.xsd">
<ab>
    <b/>
    <a/>
</ab>
<abc>
    <c/>
    <b/>
    <a/>
</abc>
</root>

5 Answers 5

19

There is a way to do this that relies on the fact that a choice within a choice acts as a bigger choice.

First, define an element group which contains a single choice from all the elements within the base element:

<xs:group name="common_ab_elements">
    <xs:choice>
        <xs:element name="a"/>
        <xs:element name="b"/>
    </xs:choice>
</xs:group>

Then you can use this in your definition of abElement in place of the elements you had before:

<xs:complexType name="abType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:group ref="common_ab_elements"/>
    </xs:choice>
</xs:complexType>

If you need an extended type, then you can extend the choice:

<xs:complexType name="abcType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:group ref="common_ab_elements"/>
        <xs:element name="c"/>
    </xs:choice>
</xs:complexType>

This is equivalent to:

<xs:complexType name="abcType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:choice>
            <xs:element name="a"/>
            <xs:element name="b"/>
        </xs:choice>
        <xs:element name="c"/>
    </xs:choice>
</xs:complexType>

And because of the nature of the choice operation, this is in turn equivalent to:

<xs:complexType name="abcType">
    <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="a"/>
        <xs:element name="b"/>
        <xs:element name="c"/>
    </xs:choice>
</xs:complexType>

...which is what you want.

If you have attributes as well, then you may also need to define a common base class which you can extend. With this scheme, only classes with no derived classes should have elements, as it's the presence of elements on the base elements that forces the sequencing.

What we are effectively doing here is defining the inheritance of choice elements separately from the elements hierarchy itself.

Sign up to request clarification or add additional context in comments.

Comments

12

Unfortunately, the short answer is NO, you can't extend a choice compositor. Logically, if there is some sort of relationship between a, b, and c (as in Java, .NET, everything is ultimately an Object, you could do the same in XSD) then I suggest the use of substitution groups instead (or, if you prefer, something based on xsi:type).

UPDATE with an example. The XSD-1:

<?xml version="1.0" encoding="utf-8" ?>
<!--W3C Schema generated by QTAssistant/W3C Schema Refactoring Module (http://www.paschidev.com)-->
<xsd:schema targetNamespace="http://tempuri.org/XMLSchema.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:element name="ab" type="abType"/>

    <xsd:complexType name="abType">
        <xsd:sequence>
            <xsd:element ref="ExtensibleChoice-A" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>

    <xsd:element name="ExtensibleChoice-A" type="ExtensibleChoiceBaseType" abstract="true" />

    <xsd:complexType name="ExtensibleChoiceBaseType" abstract="true">
        <xsd:sequence/>
    </xsd:complexType>

    <xsd:element name="a" substitutionGroup="ExtensibleChoice-A" type="aType" block="#all"/>
    <xsd:element name="b" substitutionGroup="ExtensibleChoice-A" type="bType" block="#all"/>
    <xsd:element name="c" substitutionGroup="ExtensibleChoice-A" type="cType" block="#all"/>

    <xsd:complexType name="aType">
        <xsd:complexContent>
            <xsd:extension base="ExtensibleChoiceBaseType">
                <xsd:sequence>
                    <xsd:element name="aChild" type="xsd:string"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
    <xsd:complexType name="bType">
        <xsd:complexContent>
            <xsd:extension base="ExtensibleChoiceBaseType">
                <xsd:sequence>
                    <xsd:element name="bChild" type="xsd:int"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
    <xsd:complexType name="cType">
        <xsd:complexContent>
            <xsd:extension base="ExtensibleChoiceBaseType">
                <xsd:sequence>
                    <xsd:element name="cChild" type="xsd:string"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:schema>

Members of the substitution group

The extensibility is that at a point in time, you may have only a, b and c as members. If you, or a consumer, decide to add something (say a d element), then you simply create another schema that references the old one, with the new element d, and then use that new schema instead. The old XSD file doesn't get touched; generating new JAXB classes (as an example) will result in backward compatible code.

So, XSD-1 will validate something like this:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<ab xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
    <a>
        <aChild>aChild1</aChild>
    </a>
    <b>
        <bChild>1</bChild>
    </b>
    <c>
        <cChild>cChild1</cChild>
    </c>
</ab>

Sample XML for XSD-1

You would need something like this (XSD-2):

<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema targetNamespace="http://tempuri.org/XMLSchema1.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema1.xsd" xmlns:b="http://tempuri.org/XMLSchema.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:import namespace="http://tempuri.org/XMLSchema.xsd" schemaLocation="XSD-1.xsd"/>

    <xsd:element name="d" substitutionGroup="b:ExtensibleChoice-A" type="dType" block="#all"/>

    <xsd:complexType name="dType">
        <xsd:complexContent>
            <xsd:extension base="b:ExtensibleChoiceBaseType">
                <xsd:sequence>
                    <xsd:element name="dChild" type="xsd:string"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:schema>
  • the diagram shows the "new" list of members, d is highlighted in blue:

Extended substitution group members list

To validate this:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<ab xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:d="http://tempuri.org/XMLSchema1.xsd">
    <a>
        <aChild>aChild1</aChild>
    </a>
    <d:d>
        <d:dChild>1</d:dChild>
    </d:d>
</ab>

Sample XML showing the new member

3 Comments

How to implement inheritance with substitution groups?
Could you please explain why you are using substitution blocking?
@prasopes, there is no particular reason in this case. It general, it would depend on what you're trying to do. If you're interested to see if it applies to your case, have a look at what it means (for e.g. here ).
1

Another example with Substitutions.

XSD

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="abExtension"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="abExtension" type="abExtensionType"/>
    <xs:complexType name="abExtensionType">
        <xs:choice minOccurs="0" maxOccurs="unbounded">
            <xs:element name="a"/>
            <xs:element name="b"/>
        </xs:choice>
    </xs:complexType>
    <xs:element name="abcExtension" substitutionGroup="abExtension">
        <xs:complexType>
            <xs:complexContent>
                <xs:extension base="abExtensionType">
                    <xs:choice minOccurs="0" maxOccurs="unbounded">
                        <xs:element name="c"/>
                    </xs:choice>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
    </xs:element>
    <xs:element name="abcdExtension" substitutionGroup="abExtension">
        <xs:complexType>
            <xs:complexContent>
                <xs:extension base="abExtensionType">
                    <xs:choice minOccurs="0" maxOccurs="unbounded">
                        <xs:element name="c"/>
                        <xs:element name="d"/>
                    </xs:choice>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
    </xs:element>
</xs:schema>

Sample XML's that validate with this are

abcExtension.xml

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test.xsd">
    <abcExtension>
        <b></b>
        <a></a>
        <c></c>
    </abcExtension>
</root>

abcdExtension.xml

  <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test.xsd">
<abcdExtension>
        <a>text</a>
        <b>test</b>
        <d>text</d>
        <c>text</c>
    </abcdExtension>
</root>

abExtension.xml

     <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test.xsd">
<abExtension>
        <b></b>
        <a></a> 
    </abExtension>
</root>

2 Comments

Your answer is not satisfying the request. You're missing the scenario where you need to validate c - b - a (see the <abc/> example). By extending the element with the choice, you create "compartments"; the net result is not a true choice, where the content in the extended type can show up before the one in the base type...
@PetruGardea Agree. I just gave example as OP asked about how to use substitutiongroups in design.
1

If your focus is on extending rather than type inheritance, a <choice> can be extended by redefining a <group> as follows:

File "abc.xsd" containing the base schema:

<schema 
    xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="any" 
    xmlns:n="any" 
    elementFormDefault="qualified">

    <group name="baseGroup">
        <choice>
            <element name="a"/>
            <element name="b"/>
            <element name="c"/>
        </choice>
    </group>

    <complexType name="choiceType">
        <sequence minOccurs="0" maxOccurs="unbounded">
            <group ref="n:baseGroup"/>
        </sequence>
    </complexType>

    <element name="test">
        <complexType>
            <sequence>
                <element name="sample" type="n:choiceType" maxOccurs="unbounded"/>
            </sequence>
        </complexType>
    </element>
</schema>

File "abcdef.xsd" extending the <choice> defined in the base schema:

<?xml version="1.0"?>
<schema 
    xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="any"
    xmlns:n="any" 
    elementFormDefault="qualified">

    <redefine schemaLocation="abc.xsd">
        <group name="baseGroup">
            <choice>
                <group ref="n:baseGroup"/>
                    <element name="d"/>
                    <element name="e"/>
        </choice>
            </group>
    </redefine>
</schema>

This validates the following xml file, for instance:

<?xml version="1.0" encoding="UTF-8"?>
<test 
    xmlns="any"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="any ../schemas/abcde.xsd">

    <sample>
        <a/>
        <c/>
        <b/>
        <a/>
    </sample>

    <sample>
        <c/>
</sample>

    <sample>
    </sample>

    <sample>
        <a/>
        <e/>
        <b/>
        <d/>
        <a/>
    </sample>
</test>

Comments

0

Its Very Simple you can do using "choice" keyword and set its occurrence

<xs:element name="Product">
 <xs:complexType>
  <xs:sequence>
   <xs:choice minOccurs="1" maxOccurs="14">
   <xs:element>
   </xs:element>
   // Post your element configuration here
   </xs:choice>
  </xs:sequence>
 </xs:complexType>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.