Custom JSON Marshalling in Grails, Done “Right”

I’m waist-deep in a Grails application at work and wanted to come up for air to share something that I can’t believe “just worked.” I can’t go into details on the project, so we’re going to have to use a pretty lame example.

Let’s pretend I’m building a Recipe site, and my model consists of two classes: Recipe and User, and each Recipe belongs to one User. It’s all JSON-powered AJAX, and whenever I display a list of Recipes, I want to also display the name of the User.

Now, I could eagerly fetch recipe.user and use the default JSON converter to do a deep conversion:

However, that’d return all of the attributes of the User as part of the JSON packet. I’d never store User.password in plain text, but I’d rather not reveal things like their e-mail address.

Luckily, Grails lets you register custom “marshallers” for any given class that redefine the behavior of “as JSON.” For example, I can create a custom marshaller for Recipe that solves my problem:

That’s fine and dandy, but where should you register this thing? The example I learned from does it in BootStrap, but that smelled a bit like it’d clog things up.

I decided to put my “what would I do without Grails?” hat on, and landed on a simple Spring-based solution:

  1. Create a “CustomObjectMarshallers” class that simply held a list of “marshallers.” When its register() method is called, it calls register() on each marshaller.
  2. Within a custom marshaller, register() executes the registration code above.
  3. In BootStrap, ask Spring for the configured CustomObjectMarshallers instance and tell it to register().

Here’s the code.

Our RecipeMarshaller, in src/groovy/recipe:

Our CustomObjectMarshallers, in src/groovy/util/marshalling:

My Spring bean definitions:

And here’s line of code that makes it all happen in Bootstrap.groovy. (For information on how to get a springContext in BootStrap, see Getting Spring Beans in Grails’ BootStrap.):

Results and Conclusions

It definitely feels a bit like black magic, but this “just worked.” The first time. Seriously. I reran the initial Recipe.list() as JSON, and here’s what I saw:

Now, given that the alteration of the “as JSON” behavior happens very, very far away from the actual controller rendering results, this is definitely something I would recommend documenting before you make your teammates ask “how the hell did that happen?”

Joe Rinehart's been developing software for Web, mobile, and desktop since 1998. While he mainly now works in Java, Grails, and HTML5, he has a long history of community involvement in the Flash, Flex, and ColdFusion space. As a published author and award-winning speaker, he's bringing his skills to CompileDammit to help users new to Grails and Java. When he's not coding, he's either spending time with his family or feeding an appetite for endurance mountain bike racing. (8/9/2012 update: He's now either spending time with his family or getting over a huge knee injury caused by endurance mountain bike racing.)

Posted in Groovy/Grails
15 comments on “Custom JSON Marshalling in Grails, Done “Right”
  1. Marc Esher says:

    And easy to test, to boot!

  2. Dmitry says:

    Nice write up.
    I looked at these before and settled for creating my own converters/marshallers using a simple map. I ended up creating 3 different maps based on different UI use cases (form, tree, grid) and wrapped each one in a converFor’type’ closure to call when data is to be sent back to the client.
    It might not be as elegant as registering marshallers but it does eliminate the ‘black magic’ you alluded to in your example.
    Another issue with returning deep nested objects are the myriad of queries that are run to pickup all of the associations. By carefully constructing your marshallers (or simple maps) you can avoid the extra overhead. The downside is you have to maintain a list of properties to return. If your domain objects change your marshaller needs to change as well.

    • Joe Rinehart says:

      Agreed on all counts.

      One thing I’ve done is carefully match the marshallers to fetch strategies in the HQL that’s backing the list, avoiding the N+1 issues.

  3. jbarmash says:

    I am not a fan of your final solution, I think you could have done it more elegantly, with less changes.

    You added CustomObjectMarshallers class, added stuff to resources.groovy (where you still have to keep track of all your new marshallers), and added things into Bootstrap.after all.

    That is all to replace the two lines below:

    marshallers = [ new RecipeMarshaller() ]
    marshallers.each{ it.register() }

    I can see how you might be uncomfortable cluttering your Bootstrap, but you can create another *Bootstrap file.

    To avoid cluttering the main BootStrap.groovy, I’d create config/JSONMarshallerBootstrap.groovy, which will be also read during boostrap. This gives one class /script with a responsibility to boostrap the json converters. You eliminate having to add / modify three files that way, and create one with a single responsibility.

  4. Joe Rinehart says:

    It’s less about “uncluttering” bootstrap and more about removing implementation dependencies from the main application. Creating a custom BootStrap that does what you recommend ties the application to a given RecipeMarshaller implementation.

    Using Spring and the CustomObjectMarshallers allows you to externalize the determination of which marshallers are registered, turning it into a dependency that’s injected/configured rather than hard-coded. It’s more of a “Spring”-style solution than “Grails.”

    It’s certainly more complicated than yours, but adds flexibility.

  5. Nice write-up, Joe. Very interesting approach…

    I’m wondering, though, how does this method benefit over the slightly more explicit approach offered by the Grails documentation (save for the black-boxness of registering custom marshallers)?

    code:
    def results = Book.list()

    render(contentType: “text/json”) {
    books = array {
    for (b in results) {
    book title: b.title
    }
    }
    }

    produces:
    [
    {title:"The Stand"},
    {title:"The Shining"}
    ]

  6. jbarmash says:

    @Joe I see your point. I guess the key question is where do you maintain the list of marshallers. I suppose you can probably externalize resources.groovy so that when the server starts up, it reads it from some configured place on the prod server, so I agree that’s an advantage in some situations.

  7. jbarmash says:

    @Dan – Joe’s method using custom marshallers provides consistency across multiple controllers. In your example, you are in one controller only, but the objecs may need to be rendered in many places. You can either have a method you always call to marshal a specific object, but if you want to use the nice Grails convenience infrastructure, i.e. render books as JSON, then you need to use the marshallers approach.

    You can also defined named custom marshallers, as well as use a Marshallers plugin http://grails.org/plugin/marshallers, which is a DSL for defining marshallers.

    Named Marshallers.
    http://jwicz.wordpress.com/2011/07/11/grails-custom-xml-marshaller/

    In practice, I’ve had to use both techniques, depending on the context, even in the same JSON API. Sometimes it’s easier to rely on teh custom marshaller, sometimes for a specific endpoint you need to only expose a couple of fields, which is where the method you outlined is better.

    Hope that helps. Here is a link to the slides from a talk I gave at Gr8Conf a few weeks ago, which talks about some of the Grails plugins that exist to create RESTful APIs.

    https://github.com/sjurgemeyer/GR8-US-2012/tree/master/RESTGrailsPlugins_JeanBarmash

    • rmangi says:

      Thanks for that. I was about to post something along the lines of “while this is cool, what do you do when you need different json in different situations”, named marshallers seem to be a very flexible way to abstract this functionality and have it accessible to different controllers.

  8. Joe Rinehart says:

    Thanks for all the comments – this is one of those great cases where the comments start to contain better information than the original post!

  9. Tarun says:

    Hi,

    I have been lately facing an issue in Grails 2.1.2 version. I registered a simple custom date marshaller but at times I see that for JSON rendering Default Date marshaller is used. This is happening intermittently.

    Could you help in pointing out what could be the issue

    • Joe Rinehart says:

      Hrm – I’m assuming the Date is part of a domain model. Is the property truly a Date type, or is it something else (Calendar, Joda Time class, or maybe even a String like “2013-02-01″)?

  10. Thank You Joe for taking time and sharing this. It helped a lot. Thanks everyone for the great comments as well

  11. Todd says:

    In the interest of keeping things more “Springy”, instead of calling register in Bootstrap.groovy, you could define an init method for your customObjectMarshallers bean.

    This way, register will automagically be called when the spring context initializes, eliminating the need for calling things from Bootstrap.

    beans = {
    customObjectMarshallers( CustomObjectMarshallers ) { bean ->
    bean.initMethod = ‘register’
    marshallers = [
    new RecipeMarshaller()
    ]
    }
    }

  12. biglep says:

    Thanks a lot for sharing. Rather than deal with BootStrap.groovy, I just added a static block to the domain:

    static {
    JSON.registerObjectMarshaller(Product) {
    return [
    id : it.id,
    isActive : it.isActive,
    name : it.name,
    description : it.description,
    allergens : it.allergens,
    ingredients : it.ingredients,
    isGlutenFree : it.isGlutenFree,
    isNoSugarAdded : it.isNoSugarAdded,
    stageImageUrl : it.stageImageUrl,
    bareImageUrl : it.bareImageUrl,
    nutritionLabelImageUrl : it.nutritionLabelImageUrl
    ]
    }
    }

    This seems simple, and then has the marshaller near the actual domain object.

Leave a Reply