divB
divB

Reputation: 973

XSD: One element shall be allowed in different forms

Using XSD, I want to define a complex element which can appear in three different forms:

<Scope Name="foo" />  <!-- no children -->
<Scope Name="foo" Src="bar" /> <!-- When Src is present, there must be no children! -->
<Scope Name="foo"><!-- other children --></Scope>

In the third case, it is well defined what may appear as children elements (e.g. all three types of "Scope"). The important part is that a Scope element with a "Src" attribute must be empty!

Furhtermore, in different places, I want only specific types of elements allowed. For example, inside the root tag, I want to allow exactly one Scope element of the third type; in most cases I wand to allow all cases. And that's the problem: How to solve this?

What I did so far: I created a complex type for each of the 3 cases which I can use within . However, I can't use:

<xs:choice minOccurs="0" maxOccurs="unbounded">
    <xs:element name="Scope" type="type_Scope_WithSrc" />
    <xs:element name="Scope" type="type_Scope_WithContent" />
    <xs:element name="Scope" type="type_Scope_Base" />
</xs:choice>

I tried to create a union of those, but unions are only allowed for simpleTypes.

I also tried to define an overall type "type_Scope" which uses xs:choice to include them. But xs:choice would include xs:elements which would also require a name in this case :-(

Can you tell me how I can handle this?

Please do not tell me that it is not possible with XSD :-( :-(

Thank you

Regards divB

Upvotes: 2

Views: 565

Answers (1)

Petru Gardea
Petru Gardea

Reputation: 21658

You may get different answers, depending on whether you want to achieve this with XSD 1.1 or XSD 1.0; I would assume that you're after a 1.0 solution, which I will describe here (I don't believe 1.1 is practical yet).

If you want to preserve the element name, and vary its content, your only option here is to employ xsi:type. Instead of a choice, just use a single Scope element; make its type a complex type, with an attribute named "Name". Have the other two types extend from this base type. And you're done.

Note: I used a base abstract type as a mechanism to inform people that other types are expected to go in there; reality is it'll work even without it, by using a type_Scope_Base right from the beginning.

Type hierarchy

XSD:

<?xml version="1.0" encoding="utf-8" ?>
<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:complexType name="type_Scope_BaseA" abstract="true">
    </xsd:complexType>  

    <xsd:complexType name="type_Scope_Base">
        <xsd:complexContent>
            <xsd:extension base="type_Scope_BaseA">
                <xsd:attribute name="Name" type="xsd:string"/>      
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>  

    <xsd:complexType name="type_Scope_WithSrc">
        <xsd:complexContent>
            <xsd:extension base="type_Scope_Base">
                <xsd:attribute name="Src" type="xsd:string"/>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>

    <xsd:complexType name="type_Scope_WithContent">
        <xsd:complexContent>
            <xsd:extension base="type_Scope_Base">
                <xsd:sequence>
                    <xsd:any maxOccurs="unbounded" namespace="##other" processContents="lax"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>

    <xsd:element name="root">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="Scope"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Scope" type="type_Scope_BaseA"/>

</xsd:schema>

These are three sample XMLs, all valid. The first one with content model from type_Scope_Base.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
    <Scope xsi:type="type_Scope_Base" Name="Name1"/>
</root>

This with content model from type_Scope_WithSrc.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
    <Scope xsi:type="type_Scope_WithSrc" Name="Name1" Src="Src1"/>
</root>

And this with content model from type_Scope_WithContent.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
    <Scope xsi:type="type_Scope_WithContent" Name="Name1">
        <me:hello.you xmlns:me="http://paschidev.com"/>
    </Scope>
</root>

If you want to allow for variation in the tag name, instead of a choice you could place there the head of a substitution group, which could at least give you a solution without xsi:type.

And then there are XSD 1.1 based solutions, but I would stay away from anything like that in an open environment; not everyone today has a compliant processor, let alone the spec itself is not a recommendation yet.

Upvotes: 2

Related Questions