Mar 18th, 2010, 08:56 AM
Roo and Jackson Views
After some 2 weeks working with Roo and Jackson integration I decided to post this to the forum hoping that other people can benefit from the knowledge. I understand that there were easier ways to do what I wanted, but I like to see things working integrated and explore frameworks full capabilities.
As a mostly lazy programmer I quite enjoy clever frameworks that are easy to work with and need no tweaks at all - that's what made me work with Roo. Playing around I felt the need to have a json serialization schema set up and to my pleasant surprise there was MappingJacksonJsonView and it's magic serialization capabilities, which led me to the ORM x Serialization conumdrum. Having the classic parent-child (master-detail) setup it was not long before StackOverflowException showed up - because Child references Parent which contains a list of childs re-referencing Child. So I dug a little the serialization API - Jackson - and found the concept of Views.
Basically a view lets you determine which properties should be serialized for a particular session. The basic mechanism is setup by having marker classes - which allow for view inheritance - and adding @JsonView(Class<?>) annotations to properties, which could be methods or fields...or maybe just methods.
As Spring Roo works with aspects to ease the pain of coding getters and setters, my Entities had only the private fields, which I annotated. To my surprise, the annotations were ignored because the default Jackson serialization schema will auto-detect getters/settters AND fields and compute properties using that order of precendence. So basically, as I had getters and they were not annotated the fields were skipped because the logical properties had already been detected. Ok...
So I changed auto-detect settings: add @JsonAutoDetect(JsonMethod.FIELD) on top of my entities, @JsonProperty and @JsonView to the fields and VOILA! Jackson auto-detection will only compute fields and consider the annotations. As a side-effect only annotated fields will be serialized which is not true for default setup: Jackson will include properties that are not annotated if auto-detect is not changed.
And so I fixed the StackOverflowException issue by creating a view and excluding the child references in the parent entities, therefore, when serializing the parent reference in a Child Jackson skipped the list of children avoiding the circular reference. So I started to play around and all my .json calls would return valid json. Or not...
I actually got valid json BUT all of the parent's properties were null. What!? So now I start digging the Roo stack of code and to my surprise the objects were being passed to the serialization API with all field values...and a hibernateLazyInitialization object. As I configured Jackson to use fields as properties it's introspector will use reflection to retrieve Field objects and call Field.get() to retrieve it's value. And as I'm using lazy initialization the fields ARE actually NULL and the hibernateLazyInitialization object does not kick-in when Field.get() is called. (That's actually what I believe is happening as I haven't dug too deep into CGLibProxy stuff, but the fact is that Field.get() is returning null values)
So now I'm decided to separate things and have special classes for serialization that have no enhancement at all, but I might spend a little more time trying to setup Roo and Jackson to work with views.
Hope it helps someone.
Mar 26th, 2010, 09:26 AM
Did you look at using @JsonIgnoreProperties or @JsonWriteNullProperties annotations to address the "null" field issues?
e.g. Annotating the class with:
The former works for me. It has the downside that you need to manage the list of fields explicitly since its a class annotation rather than a field/method annotation. I haven't tried the latter annotation yet and it might have the side effect of ignoring any property that happens to be null.
Strangely, I think @JsonIgnore tells Jackson to ignore serializing the field, but not to ignore the field completely (leaving it "null").
BTW, these annotations didn't work correctly using earlier versions of Jackson, but works for me in 1.4.3
Mar 26th, 2010, 09:43 AM
@JsonIgnoreProperties works quite well to avoid the circular references. It even fixed an issue with hibernateLazySerialializer property (added by Hibernate to the entities). As it is a class annotation the property field/getter annotation issue is also resolved.
The null thing is another problem. Actually the problem is that, because I'm using lazy initialization in Hibernate and, in the reported scenario, I'm using FIELD auto detection, Jackson will serialize the property using the Field objects retrieved using reflection but the value is null because they have not yet been initialized by Hibernate.
I used Jackson 1.5.0 for my tests.
Mar 30th, 2010, 07:50 PM
and they say that properties aren't needed for java 1.7
Tags for this Thread