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, subtype5How can I represent this in XSD so that each of subtype enumerations are linked specifically to their main type? Is there a b开发者_如何学编程etter way to represent those 2 fields to make the XSD simpler? I am not opposed to changing the XML document structure.
(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:
- you have something that can be seen as a co-occurrence constraint because
<subType>
can appear only when<mainType>
is present - 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 the same context (children of
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>
You need to explicitly define your type as containing a maintype and a subtype. At each juncture, you define explicitly what is possible at that point:
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:choice>
<xs:element name="MyType1" type="MyType1Type" />
<xs:element name="MyType2" type="MyType2Type" />
</xs:choice>
</xs:complexType>
</xs:element>
<!-- MainType definitiions -->
<xs:complexType name="MyType1Type>
<xs:all minOccurs="1">
<xs:element name="MyType1SubType1" type="MyType1SubType1Type" />
<xs:element name="MyType1SubType2" type="MyType1SubType2Type" />
<xs:element name="MyType1SubType3" type="MyType1SubType3Type" />
</xs:all>
</xs:complexType>
<xs:complexType name="MyType2Type>
<xs:all minOccurs="1">
<xs:element name="MyType2SubType4" type="MyType2SubType4Type" />
<xs:element name="MyType2SubType5" type="MyType2SubType5Type" />
</xs:all>
</xs:complexType>
<!-- SubType definitions -->
<xs:complexType name="MyType1SubType1Type>
<xs:sequence>
<xs:element name="MyType1SubType1TypeValue" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="MyType1SubType2Type>
<xs:sequence>
<xs:element name="MyType1SubType2TypeValue" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="MyType1SubType3Type>
<xs:sequence>
<xs:element name="MyType1SubType3TypeValue" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="MyType2SubType4Type>
<xs:sequence>
<xs:element name="MyType2SubType4TypeValue" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="MyType2SubType5Type>
<xs:sequence>
<xs:element name="MyType2SubType5TypeValue" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
So it's a bit long winded but in XSD there are few shortcuts. You need to tie down the definitions of all the types. Hope this helps.
<type>
<name>MainTypeA</name>
<type>
<name>subtype1</name>
</type>
<type>
<name>subtype1</name>
</type>
</type>
or
<type name="MainTypeA">
...
<type name="subtype1">
...
</type>
<type name="subtype2">
...
</type>
</type>
This allows subsubtype etc infinitely. Are subtypes intrinsically different to types? If not, no need to create a separate subType
tag for them. A uniform tag name for all types at any depth will make analysis easier.
XML has so many different options. You just have to build it to your needs. For me I like using attributes when possible it's just a personal preference. Here's just one suggestion.
<types>
<type name="mainTypeA">
<subType name="subType1"></subType>
<subType name="subType2"></subType>
<subType name="subType3"></subType>
</type>
<type name="mainTypeB">
<subType name="subType4"></subType>
<subType name="subType5"></subType>
<subType name="subType6"></subType>
</type>
</types>
Another suggestion:
<type name="MainTypeA">
<type name="subType1"></type>
<type name="subtype2"></type>
</type>
<type name="MainTypeB">
<type name="subType3">
<type name="subSubType1"></type>
</type>
<type name="subtype4"></type>
</type>
精彩评论