LocalDate AS JSON and other types to custom serialize in Grails

This post describes how to create custom serializers to marshall custom types such as LocalDate and Domain class objects into JSON response. The challange is that the default JSON converter provided in Grails is unable to marshall custom types. In practice, it also useful to define how the associations is marshalled into the JSON response. For example, if there is Review associated with RentalUnit, perhaps, you wish only marshall rentalUnit Name instead Id and so on.

Quick and Dirty

One way is to add custom serializer in the controller at the time of rendering the response into JSON response as follows:

def show() {
   Review.list()
   ...
   if (result){
   //render result as JSON
            render(contentType: "text/json") {
                def reviews = array {
                    for (b in result) {
                        review title: b.title
                        review dateReceived: b.dateReceived.toString("yyyy-MM-dd")
                    }
                }
   }
   ...
}

This will create JSON response with list of review titles and dates review received as following:

[{"title":"Great Place to Stay!"},{"dateReceived":"2010-08-30"},
{"title":"Great Apartment- Highly Recommend!"},{"dateReceived":"2010-11-03"} ...]

Important to Note that ‘dateReceived’ is custom type of org.joda.time.LocalDate. With that, you can see how groovy provides such easy way to serialize into string while Grails elegant way to build it into JSON response.

Organized To Maintain

It may be a case, you would like to do JSON marshalling per Class(Type) instead at Controller level. In another words, there is custom marshaller for each different custom type in our case Domain class. Its good for Domain class like User where you don’t want JSON response to include email, age or any other personal information.
Step 1: Create Custom Marshaller
In src/groovy/, perhaps, good place to add your custom marshaller as follows:

package reviews

import grails.converters.JSON
import com.minnehahalofts.app.Review

class ReviewMarshaller {

    void register(){
        JSON.registerObjectMarshaller(Review) {Review review ->
            return [
                    title : review?.title,
                    rentalUnitId : review?.rentalUnit?.id,
                    dateReceived : review?.dateReceived.toString("yyyy-MM-dd")
            ]
        }

    }
}

This registers custom marshaller that defines how the JSON response is going to be constructed when ‘as JSON’ called. It also serializes our custom type – LocalDate

NOTE: Make sure to use Safe Navigation operator(?.), otherwise, the marshaller will break

Step 2: Aggregate Marshallers Before Bootstrapping
Next, all of the custom marshallers are kept in one place before bootstrapping each. This makes the Bootstrap.groovy more clean and independent of each custom Marshaller
Perhaps, the src/groovy/util is good locations to add as follows:

package util.marshalling

class CustomObjectMarshallers {

    List marshallers = []

    def register() {
        marshallers.each{ it.register() }
    }
}

CustomObjectMarshallers has a list of all marshallers with an API to register each

Step 3: Define Spring Bean
In the conf/Spring/resource.groovy, we define the spring bean for CustomObjectmarshaller, so its available at the bootstrapping

import util.marshalling.CustomObjectMarshallers
import reviews.ReviewMarshaller

// Place your Spring DSL code here
beans = {
     customObjectMarshallers( CustomObjectMarshallers ) {
         marshallers = [
                 new ReviewMarshaller()
                 new SomeOtherMarshaller()
         ]
     }
}

Here we inject all of our custom marshallers as we keep adding more

Step 4: Bootstrapping to Register
At last, we register each custom marshaller at the application start up as follows:

class BootStrap {

    def loadDomainDataService
    def grailsApplication

    def init = { servletContext ->

        def applicationContext = grailsApplication.mainContext

        //register Custom marshallers
        applicationContext.getBean( "customObjectMarshallers" ).register()
...

As you can see, in Bootstrap.groovy we grab the Spring application context and retrieve our Custom Marshall aggregator – customObjectMarshallers that registers each of our custom marshallers

LocalDate Goes Global

We can follow this way and create custom marshaller just for the custom type – org.joda.time.LocalDate, however. Its already done and available as Joda-Time Plugin

Useful Links:

  • https://github.com/robfletcher/grails-gson
  • https://sites.google.com/site/gson/gson-user-guide#TOC-Custom-Serialization-and-Deserialization
  • http://compiledammit.com/2012/08/16/custom-json-marshalling-in-grails-done-right/
  • http://grails.org/plugin/marshallers
  • http://jwicz.wordpress.com/2011/07/11/grails-custom-xml-marshaller/
  • http://adhockery.blogspot.com/2009/08/json-rendering-your-classes.html
  • http://grails.org/plugin/joda-time

Leave a Reply

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