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…
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.
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.
thanks Dave, for your answer at SO and here.
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..
thanks Dave. I ran into this problem recently. This resolved it.
Tried the same approach for XML and I get this:
1luis
I guess this is expected. A better solution? Thank You.
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!
No need to recode, if you see the example in this post it uses bean utils to find the properties.
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?
Forgot to add relevant info: Grails v1.2.3
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
}
Wow, well done for finding that! An old bug maybe?
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.
Thanks for following up, I’ll look into it and update this post when I have a moment!
Great post, thanks.
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
This is very helpful,
Thank you very much it saved me a few hours.
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
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!
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 …
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.
This is a pretty old post, but a great one. What is a good option nowadays for including/excluding properties, and rendering deeper object graphs? Thoughts on http://grails.org/plugin/marshallers ?
+1 for http://grails.org/plugin/marshallers
Dave, when i use the code
Error : when i tried the code in BootStap i got the below error
Code
JSON.registerObjectMarshaller(
new DomainClassMarshaller(true), 2)
Error
Could not find matching constructor for: org.codehaus.groovy.grails.web.converters.marshaller.json.DomainClassMarshaller(java.lang.Boolean)