Search code examples
xmlxsdstylesheet

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


Solution

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