Processing Versioned XML Documents
In my previous post, I discussed two approaches that I found useful for
versioning XML schemas. The second approach in that post involved
redefining all types from previous versions into later versions without actually importing those types. As this involves no type reuse, if you are using schema-driven code-generation tools like
XmlBeans, you will notice that, each schema version leads to a different set of interfaces/classes.
This is a problem, particularly, if you are dealing with configuration files, deployment descriptors, etc. For backwards compatibility sake, applications must be able to process instance documents conforming to different versions. At the same time, for code-maintainability sake, you don’t want to have to maintain separate code paths to process each version.
Continuing with the example introduced my previous post, let us assume that the first version of the schema is declared in a "my:v1" namespace. Based on STAX, the solution involves the following steps:
- Design a new version of the schema that is backwards compatible.
Assume that this version is declared in "my:v2" namespace - Create a class Implementing the javax.xml.stream.XMLStreamReader, or extending javax.xml.stream.util.StreamReaderDelegate.
- When this implementation encounters a V1 namespace URI, let it return a V2 namespace
- Use this implementation of XMLStreamReader to process XML
Here is a code snippet of this custom XMLStreamReader.
public class VersioningStreamReader extends javax.xml.stream.util.StreamReaderDelegate
{
VersioningStreamReader(XMLStreamReader source)
{
super(source);
}
private String transform(String uri)
{
// If the uri is the namespace URI of a previous version
// return the current verion's namespace URI
return ...;
}
public String getNamespaceURI()
{
return transform(super.getNamespaceURI());
}
public String getNamespaceURI(int index)
{
return transform(super.getNamespaceURI(index));
}
public String getNamespaceURI(String prefix)
{
return transform(super.getNamespaceURI(prefix));
}
public NamespaceContext getNamespaceContext()
{
// Return a custom NamespaceContext implementation that does a similar transformation
}
}
If you are using XmlBeans, you could use the following code snippet to parse instance documents conforming to previous versions with XmlBeans generated from the current version of the XML schema. Here is a code snippet.
XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader reader = factory.createXMLStreamReader(inputStream); VersioningStreamReader vReader = new VersioningStreamReader(reader); v2.EmployeeDocument doc = v2.EmployeeDocument.Factory.parse(vReader);
The basic idea is to report instance documents conforming to older
versions of the schema conforming to the current version of the schema, such that the application processing the XML need only concern itself with the latest version of the schema. As you create later versions of this schema, all you have to do is to update the above stream reader to map all older namespaces to the current namespace.
This approach eases the pain of supporting backwards compatibility
without actually maintaining multiple code paths one for each version of the schema. You may be do similar mapping with other XML parsers, but I found it easier to use STAX.
Note: In this example, I used the STAX implementation and XmlBeans distribution from the
Diablo beta of WebLogic Server.
Update on 04/02/05: Note that the VersioningStreamReader above is incomplete. It does not implement all the methods required to correctly transform namespaces. It just illustrates the point.



Comment received from Aleksander Slominski
similiar technique can be applied when dealing with multiple versions of WS-Addressing (as used in Apache WSRF that is based on XmlBeans) but for WSA there is need to do more checking as XML Schemas are not backward compatible AFAIR so one needs to detect and fail if transforming outdated WSA version construct that is no longer supported …
alek
ps. there is a little typo in the post: “getNemespaceURI”
Thanks for correcting the typo.