Wednesday, June 5, 2013

Jersey, JAXB and json with generics and inheritance

A month or two ago I ran into an issue where I was marshalling Objects to json using Jersey with JAXB annotations on my pojos but only the Super Class attributes were being marshalled. I have uploaded some code to github to demonstrate this:

https://github.com/nebnagrom/jaxbInheritance

In this contrived example we have a Super Class House which has a member variable which is a String and an @XmlRootElement annotation. There are three child Classes of House:
  • BlueHouse which has an additional Integer
  • RedHouse which has an additional String
  • GreenHouse which has an additional Boolean
We then also have a HousingEstate<T extends House> which wraps a typed collection of houses. In this example it has no behavior but in the situation I had we had behavior on this collection as well.

The issue occured once I started trying to return HousingEstate objects from my RESTful resource. When you invoked the resource you only got back member variables from the House class, i.e. something like this:

 {"houses":[{"houseName":"first blue"},{"houseName":"2 blue"}]}

 When what I wanted was more like:

{"houses":[{"houseName":"first blue", "blueAttribute":1},
           {"houseName":"2 blue", "blueAttribute":2}]}
 
After a bit of digging I found that I needed to add another annotation on the House class - @XMLSeeAlso like this:

@XmlSeeAlso({ GreenHouse.class })

After I added that I started getting the results I wanted.

I also tried this without generics or just using the Super Class but this did not work either. Interestingly I found that:
  1. Using no generics meant that I got House member variables only, even when I annotated BlueHouse with annotation and added BlueHouses to the collection
  2. Using the parent class as the Generic gave me Marshalling exceptions, probably to be expected though.
I haven't really tried to dig into what is going on here, I assume that there is something with generics and type erasure that makes it impossible for JAXB / Jackson to know that there is a child class that it should be looking at for the marshalling.

Stack trace of error you get with the House based version over the fold:




[javax.xml.bind.JAXBException: class bmorgan.jaxbInheritance.model.BlueHouse nor any of its super class is known to this context.]
        at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerI
mpl.java:317)
        at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(Marshalle
rImpl.java:161)
        at com.sun.jersey.json.impl.BaseJSONMarshaller.marshallToJSON(BaseJSONMa
rshaller.java:106)
        at com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider.writ
eTo(JSONRootElementProvider.java:143)
        at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo
(AbstractRootElementProvider.java:157)
        ... 25 more

No comments:

Post a Comment