Grails ProTip: Dynamically creating JSON in Grails 2.0 with JsonBuilder
In January I did a talk enumerating all the features in Grails 2.0 that I was excited about and one topics that made the list was the new JsonBuilder that came with the inclusion of Groovy 1.8.
The new groovy.json.JsonBuilder is the third incarnation of its ilk replacing the grails.web.JSONBuilder, which in-turn replaced the grails.util.JSonBuilder of the long-ago days of Grails 0.2 . ( Note the subtile but important change in package name and capitalization of the builder name )
So what’s different?
The previous versions built the JSON in a similar fashion to the way the MarkupBuilder builds up html. Basically each closure build is a node in the graph and you can iterate over collections to build out a collection of nodes. This is very handy.
The one thing that these older builders failed to account for was that, in JSON, collections are not sub-nodes nested under a parent node, like we would see in output form an groovy.xml.MarkupBuilder.
This article does a really good job describing what is going on under the hood, the issue with collections, and why the current mindset of iteration over collections to build out your JSON will simply not work.
The author gives a example of what you need to do to work collection using the JsonBuilder but he fails to show properly the most useful use case in a Grails application, which is…
Converting a graph of objects into JSON
In the code example below we have two classes the make up a simple object graph. Basically a Person has multiple Addresses.
The key to working with collections is to convert them a list of maps via the collect closure. The ‘collect’ closure is ideal here because we can iterate over the collection, whilst transforming each entity into a proper key-value pair and adding it to the list that is the result of the closure.
The nice thing is that this can be done dynamically, and right within the builder.
Running the above code will yield nice, syntactically correct, JSON when we call the toPrettyString() method on the builder like this: