Saturday, January 16, 2010

Redefining an XML Schema Group

As part of our attempt to implement a form of SOA in our organization, we have a group of services who define their message formats for requests and responses by extending a base schema.

To illustrate such a scenario with a trivial example, every message consumed or produced by a service must have a root element named message, followed by a either a request or response element. A likely-less-than-perfect OO analogy might consider the message as an abstract class, extended by a request or response class.

A case was made for a particular service to be able to handle a third message type, so in this post I'll hope to demonstrate how to extend the base schema to add the new type. Here is a simplified example schema definition for the base message, BaseMessage.xsd:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="message" type="messageType" />
<xs:group name="messageGroup">
<xs:choice>
<xs:element name="request" type="requestType" />
<xs:element name="response" type="responseType" />
</xs:choice>
</xs:group>
<xs:complexType name="messageType">
<xs:sequence>
<xs:group ref="messageGroup" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="requestType" abstract="true" />
<xs:complexType name="responseType" abstract="true" />
</xs:schema>

The requestType and responseTypes are defined as abstract here, leaving it to each service to define what its request and response should look like. While this schema may seem to lack any true substance, it does allow for each service in the group to have the same base message structure so they can be processed or handled in a common way.

Moving towards the point of this post, I found adding a thrid type to the messageGroup group in an implementing schema a bit tricky. While I don't remember everything I tried before I got to the solution, I can provide an example of how I eventually added the additional type.

The redefine element is used to define what schema is being extended with the redefinition of the group messageGroup so the new type can be added. Additionally, a concrete definition of the abstract request and response element types are provided.

The third element added to the group, info, has its correspoding type defined outside of the redefine element:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns="http://schemas.org/MyService" targetNamespace="http://schemas.org/MyService" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:redefine schemaLocation="http://schemas.org/BaseMessage-1.0xsd">
<xs:element name="message" type="messageType" />
<xs:group name="messageGroup">
<xs:choice>
<xs:group ref="messageGroup" />
<xs:element name="info" type="infoType" />
</xs:choice>
</xs:group>
<xs:complexType name="requestType">
<xs:complexContent>
<xs:extension base="requestType">
<xs:sequence>
<xs:element name="file" type="xs:string" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="responseType">
<xs:complexContent>
<xs:extension base="responseType">
<xs:sequence>
<xs:element name="result" type="xs:string" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:redefine>
<xs:complexType name="infoType">
<xs:sequence>
<xs:element name="info" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>

The type definitons above are of course are kept trivial above to help highlight how the redefinition of the target group was done.

Hope this helps anyone looking to redefine a group in a schema. Please feel free to share any alternatives to the method I used above or to point out to me where I may have misspoke with respect to XML schema itself.