December 14, 2009

How to handle XML Schema Version?

Use the Schema verison attribute.
Pros:
  • Simple
Cons:
  • No XML Schema enforcement when stepping version and version number is concealed for user.
Recommendation:
  • Do no use.

Change the Schema location and keep the namespace.
Pros:
  • Good för maintaining backward compability. No depending XML files needs to be altered, simply upgrade implementing jar.
Cons:
  • Cannot tell from the XML file which implementing jar version is used.
Recommendation:
  • Use this technique to handle minor version upgrade.

Example:
http://camel.apache.org/schema/spring/camel-spring-1.6.X.xsd, in version 1.6.0-1.6.3 is the same targetNamespace used.

Change the Schema location and the namespace.
Pros:
  • Forces the user to upgrade the client code, when stepping version. Hard versioning control, no chanse to make misstake.
Cons:
  • No seamless upgrade. Upgrade will cost.
Recommendation:
  • Use this when stepping major version.

For you using JAXB, is it a good reminder that the namspace versionnumber is direclty reflected in the package name. So when uppgrading and if the depending generated code has changed namespace, i.e. package name will the depending code not compile. This can be a advantage since it will directly be apparent where in your code you have the dependencies.

XML Schema Design Pattern - Handling Versioning and Reuse

In my last project I have been working with service orientated integration and ESB, in such an architecture the domain model is represented with a canonical model of a XML schema. There several way you can design your schema which I will describe bellow, but what is more imported is the implication it has on:
  • Level of possibility to reuse your canonical model in different service.
  • Level of possibility to handle different version of same service.
  • Level of possibility to split your canonical data model into bit of pieces and process them in parallel.
Before laying out the different patterns I will explain different definition that I will use when evaluating the different patterns

Definition:
Root element – the first element a XML must contain. If a schema contains several root element it is possible to slice the XML document into several new XML document.

Global element – the elements that directly comes after the root element, possibly elements are 'element', 'complexType' and 'simpleType'.

Local element – nested element inside global element.

Russian Doll Example

Pros:
  • One root element, good for encapsulation.
  • All elements are local and encapsulated.
Cons:
  • No reuse of elements.
  • Easier to get started with.

Example Book-RussianDoll-v1.0.0.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-russiandoll/v1_0_0"
     xmlns:russ="http://ns.msc.se/examples/book-russiandoll/v1_0_0" 
     elementFormDefault="qualified">
 
 <xs:element name="book">
  <xs:complexType>
   <xs:sequence>
    <xs:element name="Title" type="xs:string" />
    <xs:choice>
     <xs:element name="AuthorList" minOccurs="0" maxOccurs="unbounded">
      <xs:complexType>
       <xs:sequence>
        <xs:element name="Author">
         <xs:complexType>
          <xs:sequence>
           <xs:element name="givenName" type="xs:string" />
          </xs:sequence>
         </xs:complexType> 
        </xs:element>
       </xs:sequence>
      </xs:complexType>
     </xs:element>
    </xs:choice>
   </xs:sequence>
  </xs:complexType>
 </xs:element> 
 
</xs:schema>

Salami Slide
Pros
  • Many root elements, the XML file can be sliced into numerous ways.
  • All elements are global, which allows reuse in other Schemas.

Cons
  • Does not encapsulate and hide the schema.
  • Usage of namespaces are almost a most, which makes usage more complex.

Example Book-SalamiSlice-v1.0.0.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-salamislice/v1_0_0"
     xmlns:salv1_0_0="http://ns.msc.se/examples/book-salamislice/v1_0_0" 
     elementFormDefault="qualified">

 <xs:element name="book">
  <xs:complexType>
   <xs:sequence>
    <xs:element ref="salv1_0_0:Title" />
    <xs:element ref="salv1_0_0:AuthorList" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>

 <xs:element name="Title" type="xs:string" />

 <xs:element name="AuthorList">
  <xs:complexType>
   <xs:sequence>
    <xs:element ref="salv1_0_0:Author" minOccurs="0" maxOccurs="unbounded" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>

 <xs:element name="Author">
  <xs:complexType>
   <xs:sequence>
    <xs:element ref="salv1_0_0:GivenName" />
    <xs:element ref="salv1_0_0:Surname" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>
 
 <xs:element name="GivenName" type="xs:string" />
 
 <xs:element name="Surname" type="xs:string" />
 
</xs:schema>

When generating code via JAXB, wee can see that several classes are annotated with the @XmlRootElement. This says that we have several possible root element.

In the next example we show how to handle version and extending previous schema version.

Example Book-SalamiSlice-v1.0.1.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-salamislice/v1_0_1"
     xmlns:salv1_0_0="http://ns.msc.se/examples/book-salamislice/v1_0_0"
     xmlns:salv1_0_1="http://ns.msc.se/examples/book-salamislice/v1_0_1" 
     elementFormDefault="qualified">
 
 <xs:import namespace="http://ns.msc.se/examples/book-salamislice/v1_0_0" schemaLocation="Book-SalamiSlice-v1.0.0.xsd" />

 <xs:element name="book">
  <xs:complexType>
   <xs:sequence>
    <xs:element ref="salv1_0_1:Title" />
    <xs:element ref="salv1_0_1:Category" />
    <xs:element ref="salv1_0_0:AuthorList" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>
 
 <xs:element name="Title">
  <xs:simpleType>
   <xs:restriction base="xs:string">
    <xs:minLength value="3" />
   </xs:restriction>
  </xs:simpleType>
 </xs:element>
 
 <xs:element name="Category" type="xs:string" />
 
</xs:schema>

Venetian Blind

Pros:
  • Only one root element, good for encapsulation.
  • All element are global, which allows reuse in other Schemas.
Cons:
  • Usage of namespaces are almost a most, which makes usage more complex.

Example Book-VenetianBlind-v1.0.0.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-venetianblind/v1_0_0"
     xmlns:ven="http://ns.msc.se/examples/book-venetianblind/v1_0_0" 
     elementFormDefault="qualified">

 <xs:element name="Book">
  <xs:complexType>
   <xs:sequence>
    <xs:element name="Title" type="ven:TitleType" />
    <xs:element name="AuthorList" type="ven:AuthorListType" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>
 
 <xs:simpleType name="TitleType">
  <xs:restriction base="xs:string">
  </xs:restriction>
 </xs:simpleType>

 <xs:complexType name="AuthorListType">
  <xs:sequence>
   <xs:element name="Author" type="ven:AuthorType" minOccurs="0" maxOccurs="unbounded" />
  </xs:sequence>
 </xs:complexType>
 
 <xs:complexType name="AuthorType">
  <xs:sequence>
   <xs:element name="GivenName" type="ven:GivenNameType" />
   <xs:element name="Surname" type="ven:SurnameType" />
  </xs:sequence>
 </xs:complexType>
 
 <xs:simpleType name="GivenNameType">
  <xs:restriction base="xs:string">
  </xs:restriction>
 </xs:simpleType>

 <xs:simpleType name="SurnameType">
  <xs:restriction base="xs:string">
  </xs:restriction>
 </xs:simpleType>
 
</xs:schema>

Example Book-VenetianBlind-v1.0.1.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-venetianblind/v1_0_1"
     xmlns:ven1_0_0="http://ns.msc.se/examples/book-venetianblind/v1_0_0"
     xmlns:ven1_0_1="http://ns.msc.se/examples/book-venetianblind/v1_0_1" 
     elementFormDefault="qualified">

 <xs:import namespace="http://ns.msc.se/examples/book-venetianblind/v1_0_0" schemaLocation="Book-VenetianBlind-v1.0.0.xsd" />

 <xs:element name="Book">
  <xs:complexType>
   <xs:sequence>
    <xs:element name="Title" type="ven1_0_1:TitleType" nillable="true" />
    <xs:element name="Category" type="ven1_0_1:CategoryType" nillable="true" />
    <xs:element name="AuthorList" type="ven1_0_0:AuthorListType" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>
 
 <xs:simpleType name="TitleType">
  <xs:restriction base="xs:string">
  </xs:restriction>
 </xs:simpleType>

 <xs:simpleType name="CategoryType">
  <xs:restriction base="xs:string">
   <xs:enumeration value="SPORT" />
   <xs:enumeration value="COOKING" />
   <xs:enumeration value="PROGRAMMING" />
  </xs:restriction>
 </xs:simpleType>
 
</xs:schema>

Conclusion
When starting of thinking of Schema one come to the conclusion that schema design is much like tradition OO design, where one must always consider if a properties and method should be hidden or exposed to other classes. The same yields for Schemas and in the real world one would scarcely use just one design pattern, but rather combine them.