Bending JAXB to the will of Scala (Part 1 of 2)

Martin Krasser has written the definitive guide to JAXB in Scala. This blog post is the first in a two part series that bottoms out solutions for the corner cases that I ran into whilst appropriating all of his hard work.

This week I'll focus on optionals. Next week I will cover lists.

Background

The use-case that I am exploring in this series of blog posts was modelling the Atom Syndication Format in appropriately typed Scala Case Classes that serialize to the corresponding XML as cleanly and concisely as possible. My (nearly complete) implementation is available here.

I wanted to avoid scala-xml because of the (perhaps un-justified) bad press it gets. Jackson and json4s both have XML bindings, but they are not flexible enough to accurately map the complete Atom Specification. The only other well supported Scala xml library that I am aware of is scalaxb. I have used that before and it is very good. However,

Since none of the Scala libraries perfectly matched my requirements I decided (perhaps foolishly) to try falling back to the de-facto Java standard, JAXB.

Basic Marshalling

Basic types (strings, nested types, un-parameterised custom types) are very simple to map once you understand how to map annotations correctly in Scala (and as Martin Krasser explains very well in his blog post).

The Troubles with Optionals

Martin also gives a great example of binding an optional string to a case class but problems emerge when you actually use it.

None Behaviour on Strings

I expected,

Person("Martin Krasser", None, 30 /* haha */)

to serialize to

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
  <name>Martin Krasser</name>
  <age>30</age>
</person>

ie to leave out the Option[String] username field because it was set to None. However, despite running JDK7u45 (Martin thought JDK7u4 and up should contain a fix for this bug) I still got a nasty NPE. And then, even when I upgraded to the latest Reference Implementation version of JAXB (2.2.7) the case class would marshal to XML but left in an empty element for username despite the XMLAdapter specifically returning null.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
  <name>Martin Krasser</name>
  <username/>
  <age>30</age>
</person>

An unneccessary empty <username/> element breaks my requirement for clean and concise xml.

Optional Inner Value-Only Types

More serious problems emerged when I tried to map one very specific optional inner type. Consider the following code,

It should marshal the following case class

Person("Caoilte O'Connor", None)

to

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
  <name>Caoilte O'Connor</name>
</person>

However, for me it NPEs when using jdk7u45 (with or without adding the RI JAXB 2.2.7 as an explicit dependency). The problem is very specific and I was only able to reproduce it for types with one attribute that was @xmlValue annotated. (I have raised the bug as JAXB-1052.)

EclipseLink MOXy : A Solution for Optionals

The only solution I found for these problems was to switch JAXB implementation (as advised by Blaise Doughan in this Stack Overflow question). EclipseLink MOXy fixes both of the problems described above.

Switching was trivial. I just added a dependency on "org.eclipse.persistence" % "org.eclipse.persistence.moxy" % "2.5.2" and put the following jaxb.properties file

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

under the same package as the case classes that I was marshalling, except in the resources directory.

As an added bonus, MOXy also doesn't print the standalone attribute in the xml fragment, which was breaking my requirement for clean and concise xml and which Metro (the Reference Implementation) cannot be configured to remove. My person case class would now marshal as,

<?xml version="1.0" encoding="UTF-8"?>
<person>
  <name>Caoilte O'Connor</name>
</person>

Perfectly concise!

Mapping Other People's Types to Optionals

The previous two examples describe the problems I found with using OptionAdapter to map a type that JAXB knows how to marshal/unmarshal. Taking a custom type that JAXB doesn't know how to manage (ie one you have already created your own XmlAdapter for) and making it optional adds a whole new dimension to the problem space. You cannot simply pass your custom type as a type parameter to the OptionAdapter and let JAXB figure out the chain of transformations required (although I did try). I came up with the following solution,

There's nothing new, or even particularly pleasing about this code, but it is interesting because it was the most concise way that I was able to take an existing XmlAdapter (DateTimeAdapter) and make it optional.

I have tested that the bug described in the JavaDoc of CustomOptionAdapter has been fixed in the trunk of EclipseLink MOXy.

Afterthoughts

I decided to write about JAXB in Scala because I wanted to highlight the problems that emerge when you scratch the surface with a real life use-case. That doesn't mean I endorse the use of JAXB in Scala. I'm still very ambivalent about the library. Exactly why might become clearer in part two of this two part series when I scratch a little deeper on the problems for JAXB in Scala and talk about lists.

comments powered by Disqus