DRY JSON and XML with Grails

Have you ever tried to support both JSON and XML in your REST API with Grails? There is the very straightforward:

class MyController {
    def index = {
        def objs = ...
        withFormat {
            json {
                render objs as JSON
            xml {
                render objs as XML
            }
        }
    }
}

It works well and is trivial to implement, but it does suffer from a significant problem: you have no control over the JSON or XML generated. So if you’re serialising domain classes, any change to your internal domain model will be reflected in the public REST API. That is Not A Good Thing.

You might then look into using the JSON and XML builders instead, but you will soon discover that they have different syntax and the structure of JSON and XML is different anyway. So you find yourself writing separate code to render JSON and XML for each action that’s part of your REST API. That’s more work than you really want.

Can you somehow marry the two to get the best of both worlds: minimal coding but control over what goes into the public API? If you don’t mind a few constraints on the output, I think you can. My proposal boils down to:

  1. Transform the data in nested maps and lists of maps, filtering out anything you don’t want in the public API
  2. Use ‘... as JSON‘ for JSON responses
  3. Use a custom method for rendering the same data as XML

Let’s take an example from the grails.org application. I want to generate a list of plugins in both JSON and XML forms so that consumers have a choice of format. The first step is easy: get a list of the plugins I want to render from the database. The next step involves transforming those plugin domain instances into a hierarchical structure based on maps. Here’s the code I came up with:

class PluginController {
    ...
    protected transformPlugins(plugins) {
        return [ pluginList: plugins ?
                plugins.collect { p -> transformPlugin(p) } :
                [] ]
    }

    protected transformPlugin(plugin) {
        def pluginMap = [
                name: plugin.name,
                version: plugin.currentRelease,
                title: plugin.title,
                author: plugin.author,
                authorEmail: plugin.authorEmail,
                description: plugin.summary,
                grailsVersion: plugin.grailsVersion,
                documentation: plugin.documentationUrl,
                file: plugin.downloadUrl,
                rating: plugin.avgRating ]
            
        if (plugin.issuesUrl) pluginMap.issues = plugin.issuesUrl
        if (plugin.scmUrl) pluginMap.scm = plugin.scmUrl

        return pluginMap
    }
    ...
}

The dynamic nature of Groovy and its expressiveness make the transformation pretty simple to effect. This could probably be simplified even further if we had something similar to the bindData() method that would map all properties of a domain instance to Map entries except those specified in an exclusion list.

Rendering the plugins as JSON then becomes a simple matter of:

def plugins = Plugin.list(...)
render transformPlugins(plugins) as JSON

since JSON maps perfectly to the map structure I’ve created. XML is a trickier proposition because it doesn’t have a straightforward concept of objects and lists. We could use as XML, but then we would end up with a whole bunch of <map> and <list> elements. No, we have to use a different approach.

I plumped for using the render() method in its XML builder form:

class PluginController {
    ...
    protected renderMapAsXml(map, root = "root") {
        render contentType: "application/xml", {
            "${root}" {
                mapAsXml delegate, map
            }
        }
    }

    protected mapAsXml(builder, map) {
        for (entry in map) {
            if (entry.value instanceof Collection) {
                builder."${entry.key}" {
                    for (m in entry.value) {
                        "${entry.key - 'List'}" {
                            mapAsXml builder, m
                        }
                    }
                }
            }
            else {
                builder."${entry.key}"(entry.value, test: "test")
            }
        }
    }
    ...
}

So now rendering the plugins as XML becomes:

def plugins = Plugin.list(...)
renderMapAsXml transformPlugins(plugins), "plugins"

where the second argument is the name of the root element in the generated XML. The great thing is, this method can be applied to _any_ transformed data as it’s not specific to plugins.

One thing to note is that the mapAsXml() method assumes that the name of any map key that has a list as its value consists of ‘<name>List’. The corresponding XML becomes a parent <nameList> element with nested <name> elements for each of the list elements. This convention simplifies the whole process without being excessively onerous.

So there you are: Don’t Repeat Yourself rendering with JSON and XML. It may not be the most efficient approach computationally, but it will save a fair bit of development and maintenance time. And don’t forget that you always have the option of caching the responses.

3 thoughts on “DRY JSON and XML with Grails

  1. Adrian

    Hi Peter,

    We tried a different approach which was similar to your description, only we used some special DTOs as public API-Objects and wrote some custom converters from our internal Domain-Model.

    This had the advantage, that we were able to document the DTOs using JavaDoc and already had a response description.

    Also, we could use both standard as JSON and as XML for rendering the response.

    In our case we also had references to other DTOs within the parent DTO, where we could easily cascade the conversion to all child objects, also using a “simple” version of the child objects for a preview were possible (which included a link to the REST-URL of the full object).

  2. seb

    Hello Peter,

    I that really DRY? Wouldn’t it be better if Plugin controller extended a BaseController with thoses?

    As for the map and the Xml, we finally used xstream wich give way more control (attributes, omitingField, implicit classes) and it’s simplicity is stunning.

Leave a Reply

Your email address will not be published. Required fields are marked *