(Quick Reference)

9.1.6 Customizing Response Rendering - Reference Documentation

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith, Lari Hotari

Version: 3.0.11

9.1.6 Customizing Response Rendering

There are several ways to customize response rendering in Grails.

9.1.6.1 Customizing the Default Renderers

The default renderers for XML and JSON can be found in the grails.rest.render.xml and grails.rest.render.json packages respectively. These use the Grails converters (grails.converters.XML and grails.converters.JSON) by default for response rendering.

You can easily customize response rendering using these default renderers. A common change you may want to make is to include or exclude certain properties from rendering.

Including or Excluding Properties from Rendering

As mentioned previously, Grails maintains a registry of grails.rest.render.Renderer instances. There are some default configured renderers and the ability to register or override renderers for a given domain class or even for a collection of domain classes. To include a particular property from rendering you need to register a custom renderer by defining a bean in grails-app/conf/spring/resources.groovy:

import grails.rest.render.xml.*

beans = { bookRenderer(XmlRenderer, Book) { includes = ['title'] } }

The bean name is not important (Grails will scan the application context for all registered renderer beans), but for organizational and readability purposes it is recommended you name it something meaningful.

To exclude a property, the excludes property of the XmlRenderer class can be used:

import grails.rest.render.xml.*

beans = { bookRenderer(XmlRenderer, Book) { excludes = ['isbn'] } }

Customizing the Converters

As mentioned previously, the default renders use the grails.converters package under the covers. In other words, under the covers they essentially do the following:

import grails.converters.*

… render book as XML

// or render book as JSON

Why the separation between converters and renderers? Well a renderer has more flexibility to use whatever rendering technology you chose. When implementing a custom renderer you could use Jackson, Gson or any Java library to implement the renderer. Converters on the other hand are very much tied to Grails' own marshalling implementation.

9.1.6.2 Registering Custom Objects Marshallers

Grails' Converters feature the notion of an ObjectMarshaller and each type can have a registered ObjectMarshaller. You can register custom ObjectMarshaller instances to completely customize response rendering. For example, you can define the following in BootStrap.init:

XML.registerObjectMarshaller Book, { Book book, XML xml ->
  xml.attribute 'id', book.id
  xml.build {
    title(book.title)
  }
}

You can customize the formatting of an individual value this way too. For example the JodaTime plugin does the following to support rendering of JodaTime dates in JSON output:

JSON.registerObjectMarshaller(DateTime) {
    return it?.toString("yyyy-MM-dd'T'HH:mm:ss'Z'")
}

In the case of JSON it's often simple to use a map to customize output:

JSON.registerObjectMarshaller(Book) {
  def map= [:]
  map['titl'] = it.title
  map['auth'] = it.author
  return map
}

Registering Custom Marshallers via Spring

Note that if you have many custom marshallers it is recommended you split the registration of these into a separate class:

class CustomMarshallerRegistrar {

@javax.annotation.PostConstruct void registerMarshallers() { JSON.registerObjectMarshaller(DateTime) { return it?.toString("yyyy-MM-dd'T'HH:mm:ss'Z'") } } }

Then define this class as Spring bean in grails-app/conf/spring/resources.groovy:

beans = {
    myCustomMarshallerRegistrar(CustomMarshallerRegistrar)
}

The PostConstruct annotation will get triggered on startup of your application.

9.1.6.3 Using Named Configurations for Object Marshallers

It is also possible to register named configurations. For example:

XML.createNamedConfig('publicApi') {
  it.registerObjectMarshaller(Book) { Book book, XML xml ->
    // do public API
  }
}
XML.createNamedConfig('adminApi') {
  it.registerObjectMarshaller(Book) { Book book, XML xml ->
    // do admin API
  }
}

Then when you use either the render or respond methods you can wrap the call in a named configuration if necessary to customize rendering per request:

XML.use( isAdmin ? 'adminApi' : 'publicApi') {
    render book as XML
}

or

XML.use( isAdmin ? 'adminApi' : 'publicApi') {
    respond book 
}

9.1.6.4 Implementing the ObjectMarshaller Interface

For more complex marshallers it is recommended you implement the ObjectMarshaller interface. For example given a domain class:

class Book {
    String title
}

By default the output when using:

render book as XML

Would look like:

<book id="1">
   <title>The Stand</title>
</book>

To write a custom marshaller you can do the following:

class BookMarshaller implements ObjectMarshaller<XML> {

public boolean supports(Object object) { return object instanceof Book }

public void marshalObject(Object object, XML converter) { Book book = (Book)object converter.chars book.title } }

And then register the marshaller with:

XML.registerObjectMarshaller(new BookMarshaller())

With the custom ObjectMarshaller in place, the output is now:

<book>The Stand</book>

Customizing the Name of the Root Element

If you wish the customize the name of the surrounding element, you can implement NameAwareMarshaller:

class BookMarshaller implements ObjectMarshaller<XML>,NameAwareMarshaller {

...

String getElementName(Object o) { return 'custom-book' }

}

With the above change the output would now be:

<custom-book>The Stand</custom-book>

Outputting Markup Using the Converters API or Builder

With the passed Converter object you can explicitly code to the Converters API to stream markup to the response:

public void marshalObject(Object object, XML converter) {
  Book book = (Book)object

converter.attribute 'id', book.id.toString() converter.attribute 'date-released', book.dateReleased.toString()

converter.startNode 'title' converter.chars book.title converter.end()

}

The above code results in:

<book id="1" date-released="...">
   <title>The Stand</title>
</book>

You can also use a builder notation to achieve a similar result (although the builder notation does not work for CompileStatic):

public void marshalObject(Object object, XML converter) {
  Book b = (Book)object

converter.build { book(id: b.id) { title b.title } } }

Using the convertAnother Method to Recursively Convert Objects

To create more complex responses you can use the convertAnother method to convert associations and other objects:

public void marshalObject(Object object, XML converter) {
  Book book = (Book)object

converter.startNode 'title' converter.chars book.title converter.end()

if (book.authors) { converter.startNode 'authors' for(author in book.authors) { converter.convertAnother author } converter.end() } }

9.1.6.5 Implementing a Custom Renderer

If you want even more control of the rendering or prefer to use your own marshalling techniques then you can implement your own Renderer instance. For example below is a simple implementation that customizes the rendering of the Book class:

package myapp
import grails.rest.render.*
import grails.web.mime.MimeType

class BookXmlRenderer extends AbstractRenderer<Book> { BookXmlRenderer() { super(Book, [MimeType.XML,MimeType.TEXT_XML] as MimeType[]) }

void render(Book object, RenderContext context) { context.contentType = MimeType.XML.name

def xml = new groovy.xml.MarkupBuilder(context.writer) xml.book(id: object.id, title:object.title) } }

The AbstractRenderer super class has a constructor that takes the class that it renders and the MimeType(s) that are accepted (via the ACCEPT header or file extension) for the renderer.

To configure this renderer, simply add it is a bean to grails-app/conf/spring/resources.groovy:

beans = {
    bookRenderer(myapp.BookXmlRenderer)
}

The result will be that all Book instances will be rendered in the following format:

<book id="1" title="The Stand"/>

Note that if you change the rendering to a completely different format like the above, then you also need to change the binding if you plan to support POST and PUT requests. Grails will not automatically know how to bind data from a custom XML format to a domain class otherwise. See the section on "Customizing Binding of Resources" for further information.

Container Renderers

A grails.rest.render.ContainerRenderer is a renderer that renders responses for containers of objects (lists, maps, collections etc.). The interface is largely the same as the Renderer interface except for the addition of the getComponentType() method, which should return the "contained" type. For example:

class BookListRenderer implements ContainerRenderer<List, Book> {
    Class<List> getTargetType() { List }
    Class<Book> getComponentType() { Book }
    MimeType[] getMimeTypes() { [ MimeType.XML] as MimeType[] }
    void render(List object, RenderContext context) {
        ....
    }
}

9.1.6.6 Using GSP to Customize Rendering

You can also customize rendering on a per action basis using Groovy Server Pages (GSP). For example given the show action mentioned previously:

def show(Book book) {
    respond book
}

You could supply a show.xml.gsp file to customize the rendering of the XML:

<%@page contentType="application/xml"%>
<book id="${book.id}" title="${book.title}"/>