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

This blog post is the second in a two part series that bottoms out solutions for the corner cases that I ran into whilst getting JAXB to work in Scala. I am indebted to Martin Krasser's blog post on JAXB for providing an essential first primer on the topic.

This week I enumerate the difficulties with lists and the work arounds I found for making them manageable. You can still read the first part of the series (on optionals) here.

Lists of the reasons to consider giving up on JAXB

Lists in JAXB are where the gaps between Java and Scala start to become painful. They're also where I gave up for a week in disgust and found solace in signing up for Erik Meijer's new Haskell MOOC. I eventually succeeded with two different ways of modeling lists, only the second of which turned out suitable for implementing the Atom specification.

Containerized Lists

Martin Krasser's JAXB blog post doesn't explain how to model lists, but does link to extensive and very helpful examples in his event sourcing project.

With a little bit of tweaking I managed to get them working for me,

This code will turn,

Entry("101", 
    List(
        Person("Caoilte O'Connor"),
        Person("Martin Krasser")
    )
)

into,

<?xml version="1.0" encoding="UTF-8"?>
<entry>
    <id>101</id>
    <authors>
        <person>
            <name>Caoilte O'Connor</name>
        </person>
        <person>
            <name>Martin Krasser</name>
        </person>
    </authors>
</entry>

The most notable property of the xml is the nesting of list elements within an authors element. A lot goes on in order to achieve this containerization, so let's break it down into a list of bullets,

Unfortunately, despite how clever it is, this code isn't suitable for the Atom Feed use case because that requires XML to be produced without the container element and this requires a completely different approach which I will discuss in the next section.

Mapping Scala Collections to Java Collections for Containerless Lists

Unfortunately the XmlAdapter can only be used to model the transformation for elements in a list. You cannot use it to model the transformation of the list itself. We got around this in the previous section by transforming a Scala List (which JAXB doesn't recognise at all) into a container of lists. If you try and turn the Scala List into a Java List, JAXB falls over hard. (It recognises that the property will end up as a Java List but ignores the adapter and attempts to cast the Scala List to a Java Collection directly.)

Because of this limitation the entire approach needs to be re-thought. I tried utilizing other JAXB annotations (eg @XmlElementWrapper, @XmlPath and @XmlTransformation) but couldn't get any of them to work with Scala. I had a similar lack of success with other Scala Collection types (Seq, ArraySeq and WrappedArray). JAXB managed to marshall some to XML but was unable to unmarshall any back to types.

The only type which worked was the Scala Array. This is probably because Array has some unique (and to be honest, rather awkward) properties. Unlike most (perhaps all?) other Scala types, Array compiles down to a Java Array in byte code and is indistinguishable to Java code. This has pros and cons,

On the plus side,

On the minus side,

The comparison with reference equality can be worked around. There's a great exploration of the possibilities here. Making Arrays work in a Case Class context is trickier, because a Case Class is really just a boilerplate generator and the boilerplate generation makes no special exception for Arrays and the Java equals, hashCode and toString methods they expose.

I spent some time figuring out how to work around this. At first I got very excited to discover that Case Classes support two parameter lists and that only parameters in the first parameter section are considered for equality and hashing. (HT: Jason Zaugg, again.) However, there is no way to partially extend equals/toString/hashCode and so this didn't gain me anything as I still had to override everything. A better solution presented itself after I saw this this manual reimplementation of a case class on a Stack Overflow question about overriding the equals method on a case class. My eventual (and I believe, optimal) solution borrowed several tricks from this discussion,

This code will turn,

Entry("101", 
    Array(
        Person("Caoilte O'Connor"),
        Person("Martin Krasser")
    )
)

into,

<?xml version="1.0" encoding="UTF-8"?>
<entry>
    <id>101</id>
    <author>
        <name>Caoilte O'Connor</name>
    </author>
    <author>
        <name>Martin Krasser</name>
    </author>
</entry>

Let's consider what is going on,

It is worth re-considering the pros and cons at this point. On the plus side,

On the minus side,

Afterthoughts

I have succeeded in bending JAXB to my use-case, but at what cost? If I use JAXB Annotated Case Classes for my domain model then I,

On reflection, JAXB is not a good fit for Scala in most scenarios - especially ones where the specification is complex or could evolve. As I have nearly completely implemented the Atom spec it isn't so bad, but I would think twice before using it again.

comments powered by Disqus