I'm on a team tasked with writing a new version of a software package. The old version used XStream for serialization. For the new version, we'd like to reorganize the data model which will in turn require a new format for our output file. We need to support reading the old data formats, and there's more than one old format.
For example, in the old version I might have this data structure where each of these elements are different (non-POJO) classes:
root
+ A
+ B
+ i
+ 1
+ 2
whereas now maybe I want to refactor so that instances of A
are now children of B
, and the contents of i
are split up and moved separately:
root
+ i.1
+ C
+ i.2
+ B
+ A
+ 1
+ 2
+ B
+ A
+ 1
+ 2
I'm not sure that this represents exactly what I want to do, but that's part of my concern. We're not certain we know what data model we want, and neither can we be certain we won't change it again in the future. I'm not merely adding, removing, or renaming objects: I'd like to move/copy them around.
Xstream and JAXB are great for serializing an object when backwards compatibility is not a problem. XStream has an FAQ describing solutions for simple alterations, but I'm wondering if their recommendation of "implement your own converter" for sufficiently complicated cases implies I should just skip xstream altogether. Other people have asked related questions.
Can I use xstream/JAXB to support reading the different data formats I illustrated into the same data model? My guess is no because I'm too inexperienced to be certain.
I gave xstream a shot, and it worked. I started by registering a converter for only the top-level tag:
TopLevel topLevel = new TopLevel();
xstream = new XStream(new DomDriver());
xstream.registerConverter(new TopLevelConverter(topLevel));
xstream.alias("TopLevel", TopLevel.class);
xstream.fromXML(data);
and each converter introduces new converter(s) for each subsequent type of node, for example in TopLevelConverter:
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
while (reader.hasMoreChildren()) {
reader.moveDown();
if("SecondLevel".equals(reader.getNodeName())){
SecondLevel secondLevel =
(SecondLevel)context.convertAnother(topLevel,
SecondLevel.class,
new SecondLevelConverter(topLevel));
topLevel.add(secondLevel);
}
reader.moveUp();
}
return topLevel;
}
where SecondLevelConverter
in turn also calls convertAnother
. In this way, I was able to do whatever I wanted by passing objects through a converter's constructor: sometimes data was sent downwards for children to use and sometimes higher level's references were sent downwards so children converters could call set methods.
However, this probably looks similar to what I would have done without xstream, so I'm not sure what xstream bought me in this specific situation.