8.1.6 Responding with JSON

Using the respond method to output JSON

The respond method is the preferred way to return JSON and integrates with Content Negotiation and JSON Views.

The respond method provides content negotiation strategies to intelligently produce an appropriate response for the given client.

For example given the following controller and action:

grails-app/controllers/example/BookController.groovy

  1. package example
  2. class BookController {
  3. def index() {
  4. respond Book.list()
  5. }
  6. }

The respond method will take the followings steps:

  • If the client Accept header specifies a media type (for example application/json) use that

  • If the file extension of the URI (for example /books.json) includes a format defined in the grails.mime.types property of grails-app/conf/application.yml use the media type defined in the configuration

The respond method will then look for an appriopriate Renderer for the object and the calculated media type from the RendererRegistry.

Grails includes a number of pre-configured Renderer implementations that will produce default representations of JSON responses for the argument passed to respond. For example going to the /book.json URI will produce JSON such as:

  1. [
  2. {id:1,"title":"The Stand"},
  3. {id:2,"title":"Shining"}
  4. ]

Controlling the Priority of Media Types

By default if you define a controller there is no priority in terms of which format is sent back to the client and Grails assumes you wish to serve HTML as a response type.

However if your application is primarily an API, then you can specify the priorty using the responseFormats property:

grails-app/controllers/example/BookController.groovy

  1. package example
  2. class BookController {
  3. static responseFormats = ['json', 'html']
  4. def index() {
  5. respond Book.list()
  6. }
  7. }

In the above example Grails will respond by default with json if the media type to respond with cannot be calculated from the Accept header or file extension.

Using Views to Output JSON Responses

If you define a view (either a GSP or a JSON View) then Grails will render the view when using the respond method by calculating a model from the argument passed to respond.

For example, in the previous listing, if you were to define grails-app/views/index.gson and grails-app/views/index.gsp views, these would be used if the client requested application/json or text/html media types respectively. Thus allowing you to define a single backend capable of serving responses to a web browser or representing your application’s API.

When rendering the view, Grails will calculate a model to pass to the view based on the type of the value passed to the respond method.

The following table summarizes this convention:

ExampleArgument TypeCalculated Model Variable
respond Book.list()java.util.ListbookList
respond( [] )java.util.ListemptyList
respond Book.get(1)example.Bookbook
respond( [1,2] )java.util.ListintegerList
respond( [1,2] as Set )java.util.SetintegerSet
respond( [1,2] as Integer[] )Integer[]integerArray

Using this convention you can reference the argument passed to respond from within your view:

grails-app/views/book/index.gson

  1. @Field List<Book> bookList = []
  2. json bookList, { Book book ->
  3. title book.title
  4. }

You will notice that if Book.list() returns an empty list then the model variable name is translated to emptyList. This is by design and you should provide a default value in the view if no model variable is specified, such as the List in the example above:

grails-app/views/book/index.gson

  1. // defaults to an empty list
  2. @Field List<Book> bookList = []
  3. ...

There are cases where you may wish to be more explicit and control the name of the model variable. For example if you have a domain inheritance hierarchy where a call to list() my return different child classes relying on automatic calculation may not be reliable.

In this case you should pass the model directly using respond and a map argument:

  1. respond bookList: Book.list()
When responding with any kind of mixed argument types in a collection, always use an explicit model name.

If you simply wish to augment the calculated model then you can do so by passing a model argument:

  1. respond Book.list(), [model: [bookCount: Book.count()]]

The above example will produce a model like [bookList:books, bookCount:totalBooks], where the calculated model is combined with the model passed in the model argument.

Using the render method to output JSON

The render method can also be used to output JSON, but should only be used for simple cases that don’t warrant the creation of a JSON view:

  1. def list() {
  2. def results = Book.list()
  3. render(contentType: "application/json") {
  4. books(results) { Book b ->
  5. title b.title
  6. }
  7. }
  8. }

In this case the result would be something along the lines of:

  1. [
  2. {"title":"The Stand"},
  3. {"title":"Shining"}
  4. ]
This technique for rendering JSON may be ok for very simple responses, but in general you should favour the use of JSON Views and use the view layer rather than embedding logic in your application.

The same dangers with naming conflicts described above for XML also apply to JSON building.