There is a feature of XML Schema called substitutionGroup that is a very neat way to extend schemas. Unfortunately this only applies to elements, not attributes. But before I get to that, here’s a basic explanation of substitutionGroup.
substitutionGroup for elements
In the following example I’ve defined an element <command> based on the type “commandType”. That type in turn is a sequence that so far contains just one element, which is an <abstractCommand>. <abstractCommand> in turn is of the type “abstractExtensionType”, which is defined as an empty type.
<!-- extract from schema1.xsd --> <element name="command" type="schema1:commandType"/> <complexType name="commandType"> <sequence> <element ref="schema1:abstractCommand" /> </sequence> </complexType> <element name="abstractCommand" type="schema1:abstractCommandType" /> <complexType name="abstractCommandType" />
Now none of that is actually usable at the moment because the <abstractCommand> element doesn’t actually contain anything useful. So all we can do is this following pointless code:
<!-- extract from document1.xml --> <command> <abstractCommand/> </command>
However let’s assume this is part of a much larger document, in which case we use this as a place marker. Then we create our second schema, where we provide a concrete command. In this case we define a new element called “create”, which can be used in place of <schema1:abstractCommand> thanks to the substitutionGroup attribute. This is also based on a type that is derived from the type of the element that it is used in place of. In other words <create> is of the type “schema2:extCommandType”, which in turn is an extension of “schema1:abstractCommandType”.
<!-- extract from schema2.xsd --> <element name="create" substitutionGroup="schema1:abstractCommand" type="schema2:extCommandType" /> <complexType name="extCommandType"> <complexContent> <extension base="schema1:abstractCommandType"> <sequence> <any namespace="##other" maxOccurs="unbounded"/> </sequence> </extension> </complexContent> </complexType>
So, to finish off the explanation, we can now use all of this in a new document that uses the <schema1:command> element from the first file, but contains the “create” command specified in the second file.
<!-- extract from document2.xml --> <command> <create> <things_to_create/> </create> </command>
The final point is that if we had wanted to prevent anyone from create an <abstractCommand> element , just to be tidy, then we could have defined it using the “abstract” attribute:
<complexType name="abstractCommandType" abstract="true" />
substitutionGroup for attributes
XML Schema does not actually support substitutionGroup for attributes, but if it did then what would it look like? Well a bit like this:
<!-- extract from schema1.xsd --> <complexType name="operation"> <sequence> <any namespace="##other" /> </sequence> <attribute ref="schema1:opcode" use="required" /> </complexType> <attribute name="opcode" type="schema1:opcodeType" /> <simpleType name="opcodeType"> <restriction base="token"> <enumeration value="request"> <enumeration value="approve"> <enumeration value="reject"> <enumeration value="withdraw"> </restriction> </simpleType> <!-- extract from schema2.xsd --> <simpleType name="extendedOpcodeType"> <union memberTypes="schema1:opcodeType"> <simpleType> <restriction base="token"> <enumeration value="dispute"> </restriction> </simpleType> </union> </simpleType> <attribute name="extendedOpcode" type="schema2:extendedOpcodeType" substitutionGroup="schema1:opcode" /> <!-- extract from document1.xml --> <operation extendedOpcode="dispute"> <blah /> </operation>
I’ve proposed this to the XML Schema working group to see if they might consider it for v1.1
If you are interested in reading more about XML Schema or substitutionGroup then don’t bother looking these up in the XML Nutshell book as these only get a cursory section. Use a specialist XML Schema book instead.