Archive for January, 2009|Monthly archive page

Omit Unexpected XML Elements With XStream

XStream is a Java xml library, which nicely serializes Java objects to XML and vice versa. It can easily deal with missing (i.e. optional) XML elements. The corresponding Java fields will just be left blank.

<user>
    <name>Peter Voss<name>
</user>

can be read into the Java object:

public class User {
    private String name;
    private String role;

    // getter and setter methods are here
}

In this case the optional <role> field is missing in the XML and the corresponding field in the User Java object will be left null when deserializing the XML.

But once if you have decided on your XML API, you might want to question if it is flexible enough. Just consider you have built software based on this XML spec. Can you still add optional XML elements without breaking the applications that you have already shipped? Consider you want to add more information, like a <department> element. Will your clients be able to just ignore this piece of information? The short answer is: No. XStream will throw a ConversionException if it finds an element that has no corresponding Java field. The Jira ticket XSTR-30 is an improvement request related to this topic. But so far XStream has no simple switch to turn off complaining about unknown elements.

But you could easily tweak XStream to ignore additional elements by adding your own custom mapper that ignores the field. The following snippet creates an XStream instance that ignores additional fields. It is grabbed from the CustomMapperTest.testCanBeUsedToOmitUnexpectedElements() unit test that is part of the XStream source code:

XStream xstream = new XStream() {
  @Override
  protected MapperWrapper wrapMapper(MapperWrapper next) {
    return new MapperWrapper(next) {
      @Override
      public boolean shouldSerializeMember(Class definedIn,
              String fieldName) {
        if (definedIn == Object.class) {
          return false;
        }
        return super.shouldSerializeMember(definedIn, fieldName);
      }
    };
  }
};

I just wanted to write this down, because a solution for this common problem is somewhat difficult to find right now.