Rendering JSON in Grails. Part 2: Plain Old Groovy Objects and Domain Objects

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.

About these ads

5 thoughts on “Rendering JSON in Grails. Part 2: Plain Old Groovy Objects and Domain Objects

  1. 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

  2. Pingback: JSON Output In Grails Controller « Ryan Alberts

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