Rendering JSON in Grails: Part 3 Customise your JSON with Object Marshallers

So far I’ve shown how to produce JSON from simple collections and how to convert domain objects to JSON, in this final post in my three-part look at rendering JSON from Grails controllers, we’ll take a look at customising the way objects are rendered by the JSON converter.

What’s Wrong with the Default Converter?

The default behaviour of the JSON converter is to convert all properties of a POGO and all persisted properties of a domain object into propertyName: propertyValue JSON pairs. While it’s very useful, there are some common problems with the default conversions.

Am I Exposing Too Much?

In most situations you will not need to make all of an object’s properties available in a public API and cutting down on the number of properties means an increase in performance as you’re converting and transferring less data. There may also security issues with making your class names public or giving out domain-class ids in some applications, also while it might be fine to have all the properties of a bean public now, if in the future you add a sensitive property to a domain class can you be confident you’ll check that it’s not being included in your public API?

A Custom JSON Converter

In many applications I’ve worked on, the solution to the exposure problem is to develop a layer of adapters which wrap domain objects so only certain properties are exposed via an API. While this approach works, it can become a big development and maintenance overhead.

Thankfully, Grails provides a standard way to extend and modify JSON converters by using the ObjectMarshaller interface, below is a simple example for overriding the rendering of the “Person” class (from my previous posts) by registering a conversion closure in Bootstap.groovy:

import grails.converters.JSON
    class BootStrap {
        def init = {servletContext ->
            JSON.registerObjectMarshaller(Person) {
                def returnArray = [:]
                returnArray['name'] = it.name
                returnArray['addrs'] = it.addresses
                return returnArray
        }
}

Now rendering a Person object only shows the addresses and a name (and no class or id on the Person):

{
    "name": "David Bower",
    "addrs": [
        {
            "class": "Address",
            "id": 6,
            "person": {
                "class": "Person",
                "id": 7
            },
            "building": "25",
            "street": "High Street",
            "country": "UK"
        }
    ]
}

Notice how the Address still shows the Person class and id – it’s because it’s still using the defaults. We can override the Address class in a similar way:

        JSON.registerObjectMarshaller(Address) {
            def returnArray = [:]
            returnArray['building'] = it.building
            returnArray['street'] = it.street
            returnArray['city'] = it.city
            returnArray['country'] = it.country
            return returnArray
        }

A Generic Approach to Custom Conversions

You may decide that the default converter would be fine if only it did or didn’t do something. If so it’s easily remedied by an ObjectMarshaller and a quick look into the Grails source. Let’s assume we like the default conversion of POGOs except that we really don’t want to render the “class” property…

Better Living Through Copying Customising

In the Groovy source code the default JSON converters are defined in org.codehaus.groovy.grails.web.converters.marshaller.json. Here’s the source of the GroovyBeanMarshaller, used for non-domain Groovy objects:

/*
 * Copyright 2004-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codehaus.groovy.grails.web.converters.marshaller.json;

import grails.converters.JSON;
import groovy.lang.GroovyObject;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException;
import org.codehaus.groovy.grails.web.converters.marshaller.ObjectMarshaller;
import org.codehaus.groovy.grails.web.json.JSONWriter;
import org.springframework.beans.BeanUtils;

public class GroovyBeanMarshaller implements ObjectMarshaller {

    public boolean supports(Object object) {
        return object instanceof GroovyObject;
    }

    public void marshalObject(Object o, JSON json)
        throws ConverterException {
        JSONWriter writer = json.getWriter();
        try {
            writer.object();
            PropertyDescriptor[] properties =
                BeanUtils.getPropertyDescriptors(o.getClass());
            for (PropertyDescriptor property : properties) {
                String name = property.getName();
                Method readMethod = property.getReadMethod();
                if (readMethod != null
                    && !(name.equals("metaClass"))) {
                    Object value =
                        readMethod.invoke(o, (Object[]) null);
                    writer.key(name);
                    json.convertAnother(value);
                }
            }
            Field[] fields = o.getClass().getDeclaredFields();
            for (Field field : fields) {
                int modifiers = field.getModifiers();
                if (Modifier.isPublic(modifiers)
                    && !(Modifier.isStatic(modifiers)
                        || Modifier.isTransient(modifiers))) {
                    writer.key(field.getName());
                    json.convertAnother(field.get(o));
                }
            }
            writer.endObject();
        } catch (ConverterException ce) {
            throw ce;
        } catch (Exception e) {
            throw new ConverterException(
                "Error converting Bean with class "
                + o.getClass().getName(), e);
        }
    }

Looking at the code we can easily stop the class name being rendered by adding another test to the first if statement:

if (readMethod != null
    && !name.equals("metaClass")
    && !name.equals("class")) {

By creating a new class, “NoClassNameObjectMarshaller”, we can then register our new marshaller in Bootstrap.groovy:

    JSON.registerObjectMarshaller(new NoClassNameObjectMarshaller())

Priorities

If you were to use the NoClassNameObjectMarshaller in practice you would immediately notice that domain class rendering is broken! The problem is that we’ve registered our NoClassNameObjectMarshaller as handling all Groovy objects in the “supports” method so it also overrides the existing “DomainClassMarshaller”. This can be solved by re-prioritising the renderers in Bootstrap.groovy so the DomainClassMarshaller is checked for support first :

import grails.converters.JSON
    class BootStrap {
        def init = {servletContext ->
            JSON.registerObjectMarshaller(
                new NoClassNameObjectMarshaller(), 1)
            JSON.registerObjectMarshaller(
                new DomainClassMarshaller(true), 2)
        }
}

Notice that the higher-valued priorities are checked first (and if you don’t specify a priority it will be set to zero).

JSON and deep.JSON Confusion

In part 2 of the series I pointed out the difference between the standard JSONConverter and the deep.JSONConverter and it is important to note that if you register an object marshaller for one of the converters, it will not be picked up by the other. In other words if you want to override both the standard and deep converters you must explicitly do so in Bootstrap.groovy.

Other Grails Marshallers

The examples above give a feel for how easy it is to customise the default JSON rendering behaviour and the Grails source provides many more marshallers which I encourage you to take a look at for examples on rendering Enums, Dates, Java beans and other types.

Rendering transient fields for all domain objects could be made possible by combining the features of the DomainClassMarshaller and the GroovyBeanMarshaller, however, I’ve found it more useful to override behaviour on a class-by-class approach since the defaults are generally more friendly.

I recommend caution when overriding the rendering of all POGOs (as demonstrated above) and it may be wise to limit the support to certain packages or classes to avoid unexpected problems when rendering standard types.

All Good Things

I hope that this short series has given some insight into the practicalities of rendering JSON in real applications. Out of all the examples provided, the basic rendering of collections and registering simple custom marshallers in closures are the two techniques I use most often and find most useful. I’m still waiting to find a good reason to use a JSONBuilder…

About these ads

39 thoughts on “Rendering JSON in Grails: Part 3 Customise your JSON with Object Marshallers

  1. Thanks you very much for your time explaining the concept. it has been really useful. Going through your other posts. One qn, ObjectMarshaller approach worked great to filter out needed attributes, but any way this to override/disable in the controller. Suppose I need one more attribute from the object. Then custom mappping is the only way? I posted a qn before I see your page at http://stackoverflow.com/questions/2562684/rendering-json-in-grails-with-part-of-the-attributes-of-an-object . Can you help me to show how to get custom attributes from the object, and get in the JSON format? thanks again.

  2. Have you ever had the problem with “Misplaced Key” when using converters.deep.JSON ?

    I don’t know how to solve it!

    I want to render an AdSpaceBooking object that has a User, an AdSpace and a Date. When the rendering comes to a list of Authorities in the user it fails with a Misplaced Key exception.

    • Just found a work around. In grails-app/Config.groovy set:

      grails.converters.json.circular.reference.behaviour = “INSERT_NULL”

      The default behaviour is circular referencing, which gives the exception. It is not really necessary either..

  3. Hi there!
    Very nice post by the way…

    I’m setting up a environment for Grails and Flex, and I plan to use JSON type for the I/O between them.
    I’ve been doing some testing, in Grails with a generic CrudService I did. I can perfectly make an POGO, domain or not, into JSON, but I realized it does not include “id” and “version” in the decode process.
    I wish to have the exact domain POGO back, when I parse it back to Object(), but I couldn’t so far.

    Have you gone through anything similar?

    I don’t want to set manually those two, every conversion I do….

    Thanks in advice!

    • Hi Fred, you should get the id using the grails.converters.JSON converter (it won’t appear when using the “deep” converter), however, getting the version will require you to register your own converter to add the version, based on GroovyBeanMarshaller. If you set it at a high priority, as shown in this post, it should do the job.

      Hope that helps

    • The problem is: I dont know if the fault of not including the id and version parameters on the object, is grails’ fault or JSON’s fault.
      Making a custom Marshaller to get the params i want back from JSON, does not seem DRY, as i would have to recode all of it everytime i change a Bean. Am i wrong?

      I mean, I know FlexJson for Java has the .include aspects, which would fit for me, as I would only have to include or exclude whatever parameters I see fit, defaulting the rest of them. I just dont know if I can do it in Grails/Groovy.

      Well, thanks anyway, I should be able to solve it one way or another. Just wanted to get things done the “groovy” way>> DRY to the core!

  4. I have tried a custom JSON ObjectMarshaller from the BootStrap init() method and it never seems to get called. Here is a snippet:

    class BootStrap {

    def init = { servletContext ->

    JSON.registerObjectMarshaller(Feed) {

    println “in custom feed marshaller”

    def returnArray = [:]
    returnArray['feedType'] = it.feedType
    returnArray['manager'] = it.manager
    returnArray['operatorName'] = it.operatorName

    return returnArray
    }
    }

    When I call this from a controller:

    def feed = Feed.get(params.id)
    render(status: 200, contentType: “application/json”, text: “{ \”feed\”: ${feed as JSON} }”)

    I never see my version of the marshaller called. I am stumped.

    • Although the JSON class has been refactored since this post (the “deep” classes have been deprecated) what you’ve written certainly looks like it should work.

      However, I wonder if it’s a priority issue – try registering your marshaller with priority 1:

      JSON.registerObjectMarshaller(Feed, 1) {

      }

      Dave

      • No go. This is driving me nuts. I changed my imports from deep to plain grails.converters.JSON

        Changing priority did nothing. Should I externalize the class rather than using the closure?

  5. OK I figured it out. It was a syntax issue that did not throw an error or warning. What worked is removing the parenthesis around the call so that it looked like this is BootStrap.groovy:

    JSON.registerObjectMarshaller Feed, {
    println “Using the registered Feed Marshaller”

    def returnArray = [:]
    returnArray['feedType'] = it.feedType
    returnArray['manager'] = it.manager
    returnArray['operatorName'] = it.operatorName

    return returnArray
    }

  6. Even more follow up. It looks like the issue is that the default configuration is not always the one being used (even though I do not have any named configurations). So always do the following before marshalling:

    JSON.use(“default”)

    That works reliably now. I had to step through the debugger to understand this one.

  7. Pingback: JSON Output In Grails Controller « Ryan Alberts
  8. Pingback: Custon JSON output in Grails | Olivier Tille
  9. Pingback: Custom JSON output in Grails | Olivier Tille
  10. Pingback: Custom JSON output in Grails « oliviertille
  11. In your first code sample, you have the line:
    returnArray['name'] = it.name
    I am attempting to find out what the ‘it’ object is specifically, any documentation on the usage of it. It seems apparent in your sample but it is a hard word to search for in this context.

    • it‘ is an implicit variable in groovy, it’s the argument of the closure. I could have written the same code with an explicit argument:

      JSON.registerObjectMarshaller(Address) { addr ->
      def returnArray = [:]
      returnArray['building'] = addr.building
      returnArray['street'] = addr.street
      returnArray['city'] = addr.city
      returnArray['country'] = addr.country
      return returnArray
      }

      There’s more on it here http://groovy.codehaus.org/Closures

  12. Pingback: Grails custom XML Marshaller « J-Wicz Blog
  13. Pingback: Creating a secure REST JSON API with Grails and Spring Security in 3 easy steps « Intelligrape Groovy & Grails Blogs
  14. Pingback: Customized Grails Controller for REST « Aa Ideas
  15. Hello Dave,

    Thank you very much for this great written series about Grails and Json. It saved me hours perhaps days, because I searched the web for a sufficient explanation, but found no complete example, though some outdated ones. In the Grails docs there are a lot of snippets, but from these snippets I got no clue, how to put that in a real Application. Here at my University there is none I know, to ask. I will use your information now for a Webserver/Client/Android Task, which I’ve got to code in my Computer Science study. Now I’ll be able to solve the communication issue properly. I am happy now. =)

    Best regards,
    Semo

  16. Is there a difference with collections? It seems that when I render object, my registered marshaller is called. If I need to render list of object, then I would have thought that my marshaller would get called for each object in the list, but it appears that some default marshaller gets called instead of my registered marshaller. I was trying to go through the class hierarchy and figure out which marshelling class is getting called, but I have not found it yet. Anyone else have this issue? Thanks. BTW I am using grails 2.1.1

    • Fyi… when I run in debug mode I get the following exception:
      Misplaced object: expected mode of INIT, OBJECT or ARRAY but was DONE

      thrown from JSONWriter.object.

      • Never mind! I am using the jaxrs plugin and had an ArrayWriter customer provider registered with jaxrs accidentally. Doh!

  17. I tried a custom JSON ObjectMarshaller and like Michael Amster it did not work. Turned out I used the “deep” configuration. To get both the default and the “deep” configuration working I did this:

    Closure marshaller = { dateObject ->
    JSON_DATE_FORMAT.format(dateObject)
    }

    JSON.registerObjectMarshaller (Date, marshaller)

    ConverterConfiguration cfg = ConvertersConfigurationHolder.getNamedConverterConfiguration(
    ‘deep’, JSON.class);
    ((DefaultConverterConfiguration) cfg).registerObjectMarshaller(
    new ClosureOjectMarshaller(Date, marshaller));

    and that works …

  18. Thanks for sharing. One other idea is that one can avoid BootStrap all together. I just added this static block to my domain class:

    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 simpler, and then has the marshaller near the actual domain object.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s