Wednesday, December 28, 2005
I hate ugly hacks, but sometimes you're left with no choice. I was hacking away at the XML schema for one of our projects, and eventually settled on a neat solution. Imagine the following scenario: your system stores its configuration in XML format; the configuration defines several types of events that can occur, and all these events share the same actions. What's the most efficient way to go about it?

Borrowing a page from the object-oriented software design book, I decided to create an abstract BaseActionType. It will include some basic self-describing information (lets suppose I'd like to have an action category; I would simply add an element to the base type and override the value in each subclass.) Each subclass would describe a different type of action, for example a SendEmailActionType would extend BaseActionType, override its category with a fixed value and add fields such as server, subject etc.

Unfortunately, it appears that XML Schema only supports one of two modes of derivation: derive by extension or derive by restriction, whereas what I in fact require is a hybrid of the two. xs:extension will not allow you to override values, whereas xs:restriction will not allow you to define new elements. This is a problem I used to encounter all the time when creating XML schemas, and today it finally pissed me off enough to find a solution. I was really stumped for a while, but eventually noticed that one of the examples on the XML Schema specification was:

<xs:complexType name="length2">
 <xs:complexContent>
  <xs:restriction base="xs:anyType">
   <xs:sequence>
    <xs:element name="size" type="xs:nonNegativeInteger"/>
    <xs:element name="unit" type="xs:NMTOKEN"/>
   </xs:sequence>
  </xs:restriction>
 </xs:complexContent>
</xs:complexType>

It got me thinking: how can they be restricting a type while adding elements? Then it hit me - this is in fact a restriction on an xs:any particle! Here's the solution I came up with:

<xs:complexType name="BaseActionType">
 <xs:sequence>
  <xs:element name="Category" type="CategoryType" />
  <xs:any processContents="strict" minOccurs="0" maxOccurs="unbounded" />
 </xs:sequence>
</xs:complexType>

<xs:complexType name="EmailActionType">
 <xs:complexContent>
  <xs:restriction base="BaseActionType">
   <xs:element name="Category" fixed="Synchronous" />
   <xs:element name="Server" type="xs:string" />
   ...
  </xs:restriction>
 </xs:complexContent>
</xs:complexType>

I reckon developers who are more experienced with XML than I am already knew the answer, but since I've been using XML far more intensively than the average developer and was repeatedly stumped by the same problem I hope someone finds this useful.

Update (January 2nd, 10:26): My technological enthusiasm has an annoying tendency to turn into a display of naïveté. Specifically, the hack above seems to work just fine for Stylus Studio (any maybe other technologies, who knows?) -- but isn't really accepted by the .NET SDK xsd.exe tool. There are two issues here:

  • The tool fails to recognize fixed="value" attributes for enumerations ("Schema validation warning: Element's type does not allow fixed or default value constraint.")
  • The tool does not recognize restriction of xs:any ("Schema validation warning: Invalid particle derivation by restriction.")

I haven't been able to work around these limitations (yet), nor have I the time at the moment to research into XML Schema and find out if these features are supposed to be supported. In the meanwhile I'm reverting to another solution.

Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, strike, strong, sub, super, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Live Comment Preview