Jackson

Spring 3 MVC @RequestBody Deserialization of custom List objects

A few things to make sure Spring and Jackson can correctly deserialize a list of custom objects:

  1. Besides having annotations turned on, you must use @RequestBody in the controller method to indicate you are reading the JSON in the POST request
  2. Make sure your AJAX request has a content-type of “application/json”.  Using Ext JS 4, this is done by sending jsonData instead of params in the Ext.Ajax.request.
  3. Jackson needs the property type to know how deserialize, but Spring doesn’t pass that along in the message converters and might not ever do that,  so you need a wrapper class
  4. If Jackson cannot deserialize your class, then Spring’s MappingJacksonHttpMessageConverter.canRead<?> fails and you get the HttpMediaTypeNotSupportedException (even if the request header has content-type of application/json).  In my case, this was two setters for one property.

I had a few of these things mixed together, so it was a little tricky to unwind, but here’s some code that will deserialize a list of “Users”.  So, that JSON looks something like (w/o the proper quotes):

[{userId: 1, name: John}, {userId: 2, name: Sally}]


// The wrapper class, so that type info (of User) is passed thru the message converters to Jackson
public class Users extends ArrayList<User> {
	private static final long serialVersionUID = 1L;

	public Users() { super(); }
}

// Example method from the controller:
	@RequestMapping(value="/status", method = RequestMethod.POST)
	public @ResponseBody Map<String, ? extends Object> status(@RequestBody Users users) {
		// handle the request here
	}

Helpful links:

Ext JS – Serialize Date using Jackson and Spring 3

From bottom to top:

  1. In the database (MS SQLServer): column is a datetime (aka, a timestamp)
  2. In domain object, need the @Temporal(TemporalType.TIMESTAMP) annotation (using Hibernate 3x). Note, I also added @JsonProperty for Jackson b/c I wanted a shorter name in the JSON.
    	@Temporal(TemporalType.TIMESTAMP)
    	@Column(name="REGISTRATION_DATE")
    	private Date registrationDate;
    	@JsonProperty("regDate")
    	public Date getRegistrationDate() { return registrationDate; }
    	public void setRegistrationDate(Date registrationDate) { this.registrationDate = registrationDate; }
    
  3. Configured Jackson object mapper to use ISO-8601 format
    objectMapper.configure(SerializationConfig.WRITE_DATES_AS_TIMESTAMPS, false);
    
  4. Define field in Ext JS 4 model as a Date type, using the ‘c’ date format.
    Ext.define('User', {
    	extend: 'Ext.data.Model',
    	fields: [
    		{name: 'regDate', type: 'date', dateFormat: 'c'}
    	]
    });
    
  5. Show the date in a user friendly format in the grid. Ext JS 4, column config:
    {text: 'Registration Date', dataIndex: 'regDate', sortable: true, xtype: 'datecolumn', format:'m-d-Y'}
    

Helpful Links

The Journey for Handling Hibernate Lazy Loading using Spring 3 MVC Annotations and Jackson for JSON

I’m not even sure where to start with this one. Let’s see…

The Setup

Here’s the big picture.  A new project, new everything, lots of annotations… And I should add, I’m new to Hibernate and Spring…

  • Ext JS 4
  • Spring 3.0.4
  • Hibernate 3.6.2
  • Jackson 1.7.6

In my db, I have a User table.  Each user may have a Supervisor, which is just a user_id.  The User entity ends up looking like this:

@JsonAutoDetect
@Entity
@Table(name="PR_USER")
public class User {

	@Id
	@Column(name="USER_ID")
	private int userId;
	public int getUserId() { return userId; }
	public void setUserId(int userId) { this.userId = userId; }

	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="SUPERVISOR_ID")
	private User supervisor;
	public User getSupervisor() { return supervisor; }
	public void setSupervisor(User supervisor) { this.supervisor= supervisor; }
}

Helpful link: How to create relationship to the same entity with JPA (Hibernate)?

Issue 1 – Eagerly Load One Supervisor

Once I got it straight how to create the relationship to the same entity, everything was working well except for one thing… I only needed to eagerly load the user’s supervisor. But I didn’t need the supervisor’s supervisor and their supervisor and so on. After a lot of searching around (and I seem to have lost the link that helped me finally understand, but it might have just been the Hibernate documentation), I determined that in order to only load one “level” of supervisor I needed the “fetch=FetchType.LAZY” property on the @ManyToOne annotation for the supervisor column. Then my query needed to be built with HQL (previously I was using Criterias) so that I could specify to fetch the one supervisor eagerly. The HQL ended up looking something like this:

from User as user
left outer join fetch user.supervisor as supervisor
where user.fname like

I have a few different user queries, by first name, last name, supervisor, etc.

Great! It worked! Now my supervisor chain doesn’t go on and on. Now on to the next issue.

Issue 2 – Jackson mapping of Lazy Loaded objects

All ready to test and then bam! This error from the Jackson mapper:

org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: java.util.ArrayList[46]->....User_$$_javassist_2["handler"])
    at org.codehaus.jackson.map.ser.StdSerializerProvider$1.serialize(StdSerializerProvider.java:62)
    at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:268)
    at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:146)
    at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:118)
    at org.codehaus.jackson.map.ser.ContainerSerializers$IndexedListSerializer.serializeContents(ContainerSerializers.java:236)
    at org.codehaus.jackson.map.ser.ContainerSerializers$IndexedListSerializer.serializeContents(ContainerSerializers.java:189)
    at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:111)
    at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:296)
    at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:224)
    at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:925)
    at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.writeInternal(MappingJacksonHttpMessageConverter.java:153)

So, it turns out that Jackson does not know what to do with a Hibernate Lazy Loaded object. A lot of research and these helpful links led me to the following conclusion:

  1. There is a new, very new, completed untested Jackson-hibernate module out there to handle lazy loading, named jackson-module-hibernate.  https://github.com/FasterXML/jackson-module-hibernate.
  2. The example setup in jackson-module-hibernate has a configuration for AnnotationMethodHandlerAdapter.  Close reading of the comments in the “Serializing Hibernate entities into JSON using Jackson framwork” extremely helpful blog entry, says the AnnotationMethodHandlerAdpater configs are for REST only.  Currently, my setup is to use @ResponseBody to write the JSON the response, which uses AnnotationMethodHandlerAdapter.  So, it seems there are now two options.
    • No longer use @ResponseBody and instead create a “jsonView” and return that as the ModelAndView, as suggested in the comments of the post.
    • Make AnnotationMethodHandlerAdapter work.  This lead me to the incredibly helpful post: http://scottfrederick.blogspot.com/2011/03/customizing-spring-3-mvcannotation.html.  Which clearly explains the problem with AnnotationMethodHandlerAdapter and mvc annotations and a workaround for it, unless you can hang around for Spring 3.1.

I should also mention that I had to make changes to jackson-module-hibernate.  The key piece of code was commented out and the parameters were wrong.  So, after changing


public class HibernateSerializers implements Serializers
{
    ....
   public JsonSerializer<?> findSerializer(
        SerializationConfig config, JavaType type,
        BeanDescription beanDesc, BeanProperty beanProperty )
   {
       ....
       if (HibernateProxy.class.isAssignableFrom(raw)) {
        //  return new HibernateProxySerializer(type, config);
        //  return new PersistentCollectionSerializer(type, isEnabled(Feature.FORCE_LAZY_LOADING));
       }
      ....
   }
}

to

public class HibernateSerializers implements Serializers
{
    ....
    public JsonSerializer<?> findSerializer(
         SerializationConfig config, JavaType type,
         BeanDescription beanDesc, BeanProperty beanProperty )
    {
        ....
        if (HibernateProxy.class.isAssignableFrom(raw)) {
            return new HibernateProxySerializer( isEnabled(Feature.FORCE_LAZY_LOADING));
         // return new PersistentCollectionSerializer(type, isEnabled(Feature.FORCE_LAZY_LOADING));
        }
        ....
     }
}

With the above change to jackson-module-hibernate and also implementing Scott Frederick’s AnnotationMethodHandlerAdapterConfigurer, amazingly, it worked.  Hibernate only loaded the supervisor one level deep and Jackson handled the lazy loaded object and mapped the JSON as I expected and Ext JS showed the user data in my grid.

It was a journey…