In my last post I went through the very basics of rendering JSON in Grails. In this post I’ll cover the rendering of POGOs and Grails domain objects.
Rendering POGOs
Groovy objects are simple to render. Take this class:
class Address { String building String street String city String country }
In a controller we can add an address to a map convert it to JSON and see it rendered like this:
import grails.converters.JSON class AddressController { def jsonAddress = { def homeAddress = new Address( building: "25", street: "High Street", city: "Cambridge", country: "UK", ) def result = [address: homeAddress] render result as JSON } }
With the resulting JSON:
{ "address": { "building": "25", "street": "High Street", "class": "Address", "country": "UK", "city": "Cambridge" } }
Although that’s by far the easiest option, it’s not the only one, we can also directly render the object using a codec from a controller:
response.contentType = "application/json" render homeAddress.encodeAsJSON()
Notice that in this case we need to set the content type on a controller response, otherwise it will be rendered as HTML. Also, the output is just the fields of the object, as it’s no longer inside an “address” map:
{ "building": "25", "street": "High Street", "class": "Address", "country": "UK", "city": "Cambridge" }
Notice that the ‘as’ operator is not overloaded for plain objects so if we try to write:
render address as JSON
we get an error that looks like:
Cannot cast object 'Address@1f9aeda' with class 'Address' to class 'grails.converters.JSON'
Rendering Domain Objects
For domain objects the mechanics of rendering are similar, however, there are some domain class specific subtleties to be aware of.
Syntax
Domain objects can use the ‘as’ operator to cast an object to JSON, the same as a collection. So unlike POGOs, where they must be massaged into a list or have encodeAsJSON explictly called, it’s possible to write:
import grails.converters.JSON ... def jsonPerson = { def dave = new Person(name: "David Bower") dave.homeAddress = new Address( building: "25", street: "High Street", city: "Cambridge", country: "UK", ) def result = [person: dave] render dave as JSON }
Rendering Associations
If we render this domain class which contains embedded addresses we see exactly what we would expect:
class Person { static embedded = ['homeAddress', 'workAddress'] static constraints = { } String name Address homeAddress Address workAddress }
Gives:
{ "class": "scratch.Person", "id": 1, "homeAddress": { "building": "25", "street": "High Street", "class": "Address", "country": "UK", "city": "Cambridge" }, "name": "David Bower", "workAddress": null }
But in most cases Domain objects will contain associated objects, so what happens if we try to render a hasMany association as JSON?
class Person { static hasyMany = [addresses:Address] static constraints = { } String name }
Gives:
{ "class": "scratch.Person", "id": 10, "name": "David Bower", "addresses": [ { "class": "Address", "id": 9 } ] }
We’ve only got the id and class of the address, we’re missing the actual data. Now in some cases this may desired, if not expected, but the solution is simple…
Rendering Object Graphs Using grails.converters.deep.JSON
If we switch the rendering class in the above example from grails.converters.JSON to grails.converters.deep.JSON we see the associated objects:
{ "class": "Person", "id": 7, "name": "David Bower", "addresses": [ { "class": "Address", "id": 6, "person": { "_ref": "../..", "class": "Person" }, "building": "25", "street": "High Street", "country": "UK", "city": "Cambridge" } ] }
As you can see from the response, we also get circular references handled too.
Another way to achieve this is to add this line to your Config.groovy:
grails.converters.json.default.deep=true
which will change the behaviour of the default JSON renderer whenever it’s called.
Transient Properties
Only persisted properties will be rendered on Domain objects, so if our Person class were:
class Person { static hasyMany = [addresses:Address] static transients = ["initial"] static constraints = { } String name String initial String getInitial() { return name ? name[0] : null } }
we would not see the initial field rendered in JSON.
In the next post I’ll look at ways of controlling how the rendering is performed so the output can be customised for particular classes, answering questions such as how to stop the class name being printed and how to render transient properties on domain objects.
Thank you for this post. I was trying to test my controller, and couldn’t figure out how to get the object encoded with JSON (it worked when accessing the URL with curl, but not from the tests). I thought I was missing something obvious, and that’s when I saw that you can’t just tag on “as JSON” to plain objects.
Well, actually you can, if you’re willing to get your plain objects a little dirty. You just need to have a method called asType(Class) and convert the object there:
public class RpcResponseObject {
//…
public asType(Class clazz) {
if (clazz == JSON) {
return new JSON(this)
} else {
super.asType(clazz)
}
}
}
If it weren’t for your post, tho, I may well have given up. Thanks again
Thanks for adding that Nancy and glad my post helped you.
Kick the tires and light the fires, prlobem officially solved!
Great! I am new to Grails – Topics were really helpful and i tried all the examples –