Marshal-arts in XStream +
You might already know various ways to process XML with java objects or to transform XML into different formats. XML transformation with XSLT can be quite difficult to implement because its concept differs from usual programming approaches.
I like the framework XStream to parse and generate XML because of its simple but flexible API. You can configure the XML with annotations on plain java classes, no XML schema or DTD is required. XStream’s documentation shows you in two minutes how to use it.
We are using XStream to process XML-messages asynchronously between clients and a server, where some clients might use an older XML dialect than others. This leads to the requirement that the server must support various versions of XML messages for both receiving and sending with its clients. We use XStream not only to process the messages but for transformation as well.
In short terms, the concept:
current version: classes and annotations
former versions: classes, different configuration and converters
XML is marshaled into POJOs and back. The POJOs and their annotations fit to the current version of the XML dialect of the server.
Example for a class “Box”:
@XStreamAlias("Box")
public class Box {
Long number;
boolean defect;
boolean soiled;
// getters and setters...
}
The corresponding XML is:
<Box> <number>1</number> <defect>false</defect> <soiled>false</soiled> </Box>
Various aspects on the XML can change during evolution of the schema. I adjust the annotations and classes, but how to provide backward compatibility?
I use the same classes with a different XStream configuration. For each version I need to support, I implement the interface XStreamConfigurator. This is the interface I created to initialize an instance of XStream.
public interface XStreamConfigurator {
void configure(XStream xstream);
}
The current version, based on annotations just lists the classes that participate in XML marshaling:
xstream.processAnnotations(Box.class); // ... for each class
To provide XML marshaling/unmarshaling for other versions with the same class, I implement XStreamConfigurator differently:
Example 1: changed tags
e.g. “Pocket” instead of “Box”, “id” instead of “number”
xstream.alias("Pocket", Box.class);
xstream.aliasField("id", Box.class, "number");
This overrules the annotations.
Example 2: attribute instead of element
e.g. “defect” as attribute instead of element
xstream.useAttributeFor("defect", Box.class);
Example 3: new fields
When the current version has additional fields, you can tell XStream to omit them.
xstream.omitField(Box.class, "soiled");
Example 4: custom converter
Some classes could change so heavily, that you need to implement a converter to marshal and unmarshal XML from old version into your classes. The example supports the additional xml element “active”, that does not appear in class Box.
xstream.registerConverter(new BoxConverter_v1());
...
public class BoxConverter_v1 implements Converter {
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
Box box = (Box) source;
writeNode(writer, "id", box.getNumber(), context);
writeNode(writer, "defect", box.isDefect(), context);
writeNode(writer, "active", true, context); // additional xml element "active"
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
Box box = new Box();
while (reader.hasMoreChildren()) {
reader.moveDown();
String nn = reader.getNodeName();
if ("id".equals(nn)) {
box.setNumber((Long) context.convertAnother(box, Long.class));
} else if ("defect".equals(nn)) {
box.setDefect(Boolean.parseBoolean(reader.getValue()));
} else if ("active".equals(nn)) {
// ignore, always "true"
}
reader.moveUp();
}
return box;
}
public boolean canConvert(Class type) {
return type.equals(Box.class);
}
protected void writeNode(HierarchicalStreamWriter writer, String nodeName, Object value,
MarshallingContext context) {
if (value != null) {
writer.startNode(nodeName);
context.convertAnother(value);
writer.endNode();
}
}
}
Example 5: changed nested parts and new classes
Finally I assume that the current version wraps the “Box” element under an “Action” element, so that class “Action” contains the “Box”.
@XStreamAlias("Action")
public class Action {
private Box box;
// getter and setters...
}
XML looks like:
<Action>
<box>
<number>1</number>
<defect>false</defect>
<soiled>false</soiled>
</box>
</Action>
To configure the instance of XStream that supports the former version, I write:
xstream.registerConverter(new BoxConverter_v1()); xstream.registerConverter(new ActionConverter_v1()); // Whenever XStream encounters an instance of this type, // it will use the default implementation instead. xstream.addDefaultImplementation(Action.class, Box.class);
The ActionConverter is just a delegate to BoxConverter:
public class ActionConverter_v1 extends BoxConverter_v1 {
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
Box box = ((Action) source).getBox();
super.marshal(box, writer, context);
}
public boolean canConvert(Class type) {
return type.equals(Action.class);
}
}
So when you generate XML for an instance of Action, you call
xstream.toXML(action);
but you still get
<Pocket> <id>1</id> ... <active>true</active> </Pocket>
These examples show some possibilities for transformation with XStream. To transform old XML into current XML you unmarshal with one XStream instance and marshal with another. XStream is not restricted to generate XML, you can also generate JSON or build your own formats.

Thanks, man !
isn’t it better to go with JAXB annotations? it’s included in the latest JDKs and you can recycle the annotations also for web services with JAX-WS if you wish
<>
This article is about using XStream which – as far as I know – does not support JAXB annotations. Maybe you can you do something similar with a framework that supports JAXB annotations…?
This article shows a way for XStream-users to use the annotations to process a current XML format and to use the Java-APIs to support alternative XML (e.g. for provided backward compatibilty to the clients).
[...] – Marshal-arts in XStream saved by captainmidnite2009-04-09 – Don’t buy K&N 66 0901 X Stream Top Filter until you read [...]