Using Jaxb To Serialize Java-Objects To Xml

Recently, I had a requirement of serializing some Java objects to xml. Though I had assumed it to be fairly straight forward, during implementation, I found out the hard way that it is not at all intuitive. The following are the ground rules:

  1. You need to decorate the class which you want to serialize with the @XmlRootElement annotation. This rules out having the flexibility of serializing a List or a Map directly. It has to be wrapped within a class having the @XmlRootElement annotation.

  2. Jaxb needs a default constructor. So if you have an immutable class with all fields set through the constructor, you need to provide a default constructor and set all the final fields to null.

  3. Though Jaxb works well with the List, it needs a custom adapter to serialize a Map with the @XmlJavaTypeAdapter annotation, as we will see. This is the single most pain point in using Jaxb.

The serializer code

I am using the O/X Mappers from Spring (http://docs.spring.io/spring-ws/site/reference/html/oxm.html) for marshalling. I found it very convenient, specially if I want to switch from Jaxb to say, Xstream. The code is pretty simple:

This is the version using plain Jaxb:

The POJO to be serialized

The POJO looks like:

Attempt 1

When I try to generate xml, I get a blank document:

 

The possible cause might be that since the object is immutable without any setters for the fields, Jaxb cannot figure out the fields to serialize. So, this time we will try to explicitly ask Jaxb to serialize the fields by adding the @XmlAccessorType(XmlAccessType.FIELD) annotation to the EmployeeGroups.

Attempt 2

The POJO looks like:

The 2nd attempt gives rise to the following exception:

Caused by: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.util.List is an interface, and JAXB can’t handle interfaces.
    this problem is related to the following location:
        at java.util.List
        at private final java.util.Map com.swayam.demo.xml.EmployeeGroups.employeeGroups
        at com.swayam.demo.xml.EmployeeGroups

This message is actually misleading. What it means is that its high time for us to write our custom adapter for handling the Map.

Attempt 3

The adapter would extend the XmlAdapter. It would consume a Map<EmployeeRole, List<Employee>> and return a concrete POJO, in our case a ListWrapper<SimpleMapEntry>.

The SimpleMapEntry represents an Entry in the Map, customized to our needs.

The ListWrapper is just a wrapper around a List, as Jaxb cannot handle a List, and needs a POJO to serialize.

At the end of it, the test runs without error, giving the following output:

 

Attempt 4

The adapter works fine, but since the members are immutable, we have to explicitly declare the fields to be picked up by Jaxb.

The test errors out:

ERROR [main] PlainJaxbSerializer.serialize(46) | could not convert rmi output to xml
javax.xml.bind.MarshalException
 – with linked exception:
[com.sun.istack.internal.SAXException2: class com.swayam.demo.xml.jaxb.SimpleMapEntry nor any of its super class is known to this context.
javax.xml.bind.JAXBException: class com.swayam.demo.xml.jaxb.SimpleMapEntry nor any of its super class is known to this context.]
    at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:311)

Attempt 5

This simply means that when creating the Jaxb context, pass in the SimpleMapEntry as an argument:

All is good, and this is the output:

Attempt 6

Note that the employee node is still empty. We need to explicitly set the @XmlAccessorType(XmlAccessType.FIELD) annotation:

And everything comes as expected:

Attempt 7

The only problem as I see now is this:

It should be within an <employee>. The following is the modification needed:

With this, we get the following output:

Sources

The sources can be found here: https://github.com/paawak/blog/tree/master/code/jaxb-demo

Reference

https://jaxb.java.net/tutorial/

http://docs.spring.io/spring-ws/site/reference/html/oxm.html

Leave a Reply

Your email address will not be published. Required fields are marked *