Rendering JSON in Grails: Part 3 Customise your JSON with Object Marshallers
February 15, 2010
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…
April 2, 2010 at 3:03 am
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.
April 2, 2010 at 9:51 am
Hi, you can use the object marshaller approach if you always need to render the JSON for an object in a particular format, but if it’s just one controller that needs to render the JSON in a particular format it’s better to create arrays and maps that will produce the required output and pass that to the render method. I’ve answered your SO post to demonstrate.
April 2, 2010 at 12:28 pm
thanks Dave, for your answer at SO and here.
June 14, 2010 at 4:18 am
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.
June 14, 2010 at 4:41 am
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..
July 14, 2011 at 3:41 pm
thanks Dave. I ran into this problem recently. This resolved it.
September 12, 2010 at 4:29 am
Tried the same approach for XML and I get this:
1luis
I guess this is expected. A better solution? Thank You.
September 26, 2010 at 12:09 am
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!
September 26, 2010 at 3:45 pm
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
September 26, 2010 at 5:39 pm
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!
September 27, 2010 at 10:49 am
No need to recode, if you see the example in this post it uses bean utils to find the properties.
September 30, 2010 at 7:37 pm
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.
September 30, 2010 at 8:18 pm
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
September 30, 2010 at 8:43 pm
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?
September 30, 2010 at 8:51 pm
Forgot to add relevant info: Grails v1.2.3
October 1, 2010 at 3:42 pm
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
}
October 1, 2010 at 4:00 pm
Wow, well done for finding that! An old bug maybe?
October 1, 2010 at 5:06 pm
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.
October 1, 2010 at 8:29 pm
Thanks for following up, I’ll look into it and update this post when I have a moment!
April 19, 2011 at 1:03 am
[...] JSON Part 3: http://manbuildswebsite.com/2010/02/15/rendering-json-in-grails-part-3-customise-your-json-with-obje… [...]
May 11, 2011 at 5:19 pm
Great post, thanks.
May 13, 2011 at 3:09 am
[...] a look at “Man Builds Website” for more information. I found it really [...]
May 13, 2011 at 3:12 am
[...] a look at “Man Builds Website” for more information. I found it really [...]
May 14, 2011 at 4:08 pm
[...] a look at “Man Builds Website” for more information. I found it really [...]
June 15, 2011 at 7:56 pm
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.
June 15, 2011 at 8:35 pm
‘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
July 11, 2011 at 2:28 pm
[...] possible to use a Map to select which field is exposed in JSON/XML as shown in this post Rendering JSON in Grails: Part 3 Customise your JSON with Object Marshallers (February 15, 2010 by David [...]
August 28, 2011 at 11:22 pm
This is very helpful,
Thank you very much it saved me a few hours.
November 16, 2011 at 6:02 am
[...] sought help from David Bower’s post and created my own custom Domain Class Marshaller for JSON with a modification to just use the Enum [...]
November 22, 2011 at 5:19 pm
[...] do so. You can always generate individual controllers and modify them of course. Another option is registering custom marshallers. The simple example for customizing your JSON output without changing any controller code would be [...]
May 9, 2012 at 5:54 am
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
October 24, 2012 at 6:22 pm
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
October 24, 2012 at 7:01 pm
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.
October 24, 2012 at 7:08 pm
Never mind! I am using the jaxrs plugin and had an ArrayWriter customer provider registered with jaxrs accidentally. Doh!
November 29, 2012 at 1:17 pm
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 …