I am the type of developer that uses existing 3rd-party solutions whenever possible. Lately, I've been using XStream for object persistance in a standalone Java application. So far, I must say that I'm extremely impressed with how well this API is put together and also with its simplicity. Exporting an entire object tree is as simple as new XStream().toXML(myObjectTree);
One of the great features of XStream is its ability for customization of the XML. I began customizing my XML and ran into a slight snag (not a bug, but a snag). I couldn't figure out how to get an enum field to output as an attribute rather than a child element. Example Java class:
class Card {
int val;
Type type;
enum Type {
HEARTS, CLUBS, DIAMONDS, SPADES
}
}
Now let's export it:
void export(List<Card> cards) {
XStream xs = new XStream();
xs.alias("cards", List.class);
xs.alias("card", Card.class);
xs.useAttributeFor("val", int.class);
xs.useAttributeFor("type", Type.class);
System.out.print(xs.toXML(cards));
}
Executing the above code will produce the following XML:
<cards>
<card val="1">
<type>SPADES</type>
</card>
</cards>
As you can see, the type field is not properly used as an attribute like I instructed: xs.useAttributeFor("type", Type.class); The reason? Well, I wasn't sure, so, being the lazy developer I am, I trolled around Google, et. al. to find if someone else had this problem. Nothing. And after a few minutes of poking around in the XStream documentation, I was at a loss. I sent an email describing my problem to the users' mailing list, expecting to wait a day or so for an answer. Supprisingly, I recieved an answer within a few hours, from the project lead himself.
Jörg informed me that attribute converters must be of type SingleValueConverter. Attributes are single text values and don't need all of that other object (un)ravelling stuff. Jörg also pointed me to an issue that had been opened in the project's bug tracking system just a few days ago that also described this.
The solution, at the moment, is to simply create a specific converter for my Enum types that is a SingleValueConverter. The code for this is below:
public class SingleValueEnumConverter extends AbstractSingleValueConverter {
private final Class enumType;
public SingleValueEnumConverter(Class type) {
this.enumType = type;
}
public boolean canConvert(Class c) {
return c.equals(enumType);
}
public Object fromString(String value) {
return Enum.valueOf(enumType, value);
}
}
And to modify the XStream example above:
XStream xs = new XStream();
xs.alias("cards", List.class);
xs.alias("card", Card.class);
xs.useAttributeFor("val", int.class);
xs.useAttributeFor("type", Type.class);
xs.registerConverter(new SingleValueEnumConverter(Card.Type.class));
System.out.print(xs.toXML(cards));
And now the XML looks like this:
<cards>
<card val="1" type="SPADES"/>
</cards>
So as you can see, this was exactly what I needed. I love coding in a world where we have such a large community of people willing to take a few minutes of their time to guide you in the right direction. Great job XStream!