Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to group types & subtypes in XSD

Tags:

xml

xsd

I have XML like this:

<type>
    <mainType>...</mainType>
    <subtype>...<subtype>
</type>

The mainTypes are restricted xs:strings (a list of them). And each mainType has a list of possible subtypes. IE:
mainTypeA has possible subtypes of: subtype1, subtype2, subtype3
mainTypeB has possible subtyes of: subtype4, subtype5

How can I represent this in XSD so that each of subtype enumerations are linked specifically to their main type? Is there a better way to represent those 2 fields to make the XSD simpler? I am not opposed to changing the XML document structure.

like image 806
dontocsata Avatar asked Jan 19 '23 06:01

dontocsata


1 Answers

(This would have been a additional comment in @hugh jadick's answer but was too long for such.)

You have two problems in your initial design which complicates the structure:

  1. you have something that can be seen as a co-occurrence constraint because <subType> can appear only when <mainType> is present
  2. element declarations are inconsistent because you have
    • ...in the same context (children of <type>)
    • ...elements with same name (<mainType>)
    • ...but with different type (different value and related <subType> elements).

In XML Schema 1.0 there is no general method to make those features possible although they can be achieved in some cases and/or with some methods. One possible workaround for ambiguous type problem would be to use xsi:type attribute in the instance document to determinate the used schema type (Example 1).

Instead of using some possible workarounds to solve those problems, I suggest that you follow @hugh jadick's answer and create elements with different names for all the main types which again will have different elements for different subtypes. If you don't like having the type name as the element name (or it is not a valid for an XML element name) you could put it in an attribute, possibly using a default or fixed value (<myType1 typeName="Type 1 name">)(Example 2). If subtypes are mutually exclusive, you could put also them in an attribute (<myType1 subType="subType2">). If you really want to have the type name in the element contents (<mainType1>Type 1 name</mainType1>), then it is probably better to have the <subType> elements as following siblings instead of children of <mainType1> because that would result in mixed content which easily causes whitespace and text node position related problems (Example 3).

Yes, substitution groups could also be used with @hugh jadick's answer. You would have an abstract <mainType> element and all main type element definitions had substitutionGroup="mainType" attribute. You would still need to have different names for different main type elements because they allow different set of allowed elements/values. The type of these elements must be derived from the type of the abstract <mainType> element that they substitute (Example 4).

Code examples

Example 1

<xs:element name="type">
    <xs:complexType>
        <xs:choice>
            <xs:element name="mainType" type="mainType1"/>
            <xs:element name="mainType" type="mainType2"/>
            <xs:element name="mainType" type="mainType3"/>
        </xs:choice>
    </xs:complexType>
</xs:element>

<type>
    <mainType xsi:type="mainType1"/>
</type>

Example 2

<xs:element name="mainType1">
    <xs:complexType>
        <xs:choice>
            <xs:element ref="subType1"/>
            <xs:element ref="subType2"/>
            <xs:element ref="subType3"/>
        </xs:choice>
        <xs:attribute name="typeName" type="xs:string"/>
    </xs:complexType>
</xs:element>

Example 3

<xs:element name="type">
    <xs:complexType>
        <xs:choice>
            <xs:sequence>
                <xs:element name="mainType" type="mainType1"/>
                <xs:choice>
                    <xs:element ref="subType1"/>
                    <xs:element ref="subType2"/>
                    <xs:element ref="subType3"/>
                </xs:choice>
            </xs:sequence>
            <!-- repeat similarily for other main types -->
            <xs:sequence>
                <!-- ... -->
            </xs:sequence>
        </xs:choice>
    </xs:complexType>
</xs:element>

Example 4

<xs:element name="type">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="mainType"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>

<xs:element name="mainType" type="mainType" abstract="true"/>
<xs:element name="mainType1" type="typeForMainType1" substitutionGroup="mainType"/>
<xs:element name="mainType2" type="typeForMainType2" substitutionGroup="mainType"/>

<xs:complexType name="mainType">
<!-- definition for mainType -->
</xs:complexType>

<xs:complexType name="typeForMainType1">
    <xs:complexContent>
        <xs:restriction base="mainType">
            <!-- definition for mainType1 -->
        </xs:restriction>
    </xs:complexContent>
</xs:complexType>

<xs:complexType name="typeForMainType2">
    <xs:complexContent>
        <xs:restriction base="mainType">
            <!-- definition for mainType1 -->
        </xs:restriction>
    </xs:complexContent>
</xs:complexType>
like image 177
jasso Avatar answered Jan 31 '23 00:01

jasso