However the core of the project is extensible for potentially any kind of view implementation that is based on a Groovy DSL. The following sections cover usage of Groovy JSON views.

2. JSON Views

2.1. Introduction

JSON views are written in Groovy, end with the file extension gson and reside in the grails-app/views directory. They provide a DSL for producing output in the JSON format. A hello world example can be seen below:

2.4.2. Template Namespace

In this example, the name of the method call (person in this case) is used to dictate which template to render.

The argument to the template becomes the model. The name of the model variable is the same as the template name. If you wish to alter this you can pass a map instead:

grails-app/views/person/show.gson

model {
Person person
}
json tmpl.person(individual:person)

In the above example the model variable passed to the _person.gson template is called individual.

This technique may also be used when you want to render a template using a relative path:

grails-app/views/person/show.gson

model {
Person person
}
json tmpl.'/person/person'(person:person)

The template namespace even accepts a collection (or any Iterable object):

grails-app/views/person/show.gson

model {
List<Person> people = []
}
json tmpl.person(people)

In this case the output is a JSON array. For example:

[{"name":"Fred",age:10},{"name":"Bob",age:12}]

By passing in a collection the plugin will iterate over each element on the collection and render the template as a JSON array. If you do not want this to happen then use the variation of the method that takes a map instead:

grails-app/views/person/show.gson

model {
List<Person> people = []
}
json tmpl.person(people:people)

2.4.3. More Ways to Render Templates

The g.render method is flexible, you can render templates in many forms:

The parent template’s model will be formulated from the child templates model and the super class name. For example if the model is Person person where Person extends from Object then the final model passed to the parent template will look like:

[person:person, object:person]

If the Person class extended from a class called Mammal then the model passed to the parent would be:

[person:person, mammal:person]

This allows you to design your templates around object inheritance.

You can customize the model passed to the parent template using the model argument:

inherits template:"parent", model:[person:person]

2.5. Rendering Domain Classes

2.5.1. Basics of Domain Class Rendering

Typically your model may involve one or many domain instances. JSON views provide a render method for rendering these.

For example given the following domain class:

classBook {
String title
}

And the following template:

model {
Book book
}
json g.render(book)

The resulting output is:

{"id":1,"title":"The Stand"}

You can customize the rendering by including or excluding properties:

json g.render(book, [includes:['title']])

Or by providing a closure to provide additional JSON output:

json g.render(book) {
pages 1000
}

Or combine the two approaches:

json g.render(book, [includes:['title']) {
pages 1000
}

2.5.2. Deep Rendering of Domain Classes

Typically the g.render(..) method will only render objects one level deep. In other words if you have a domain class such as:

classBook {
String title
Author author
}

The resulting output will be something like:

{"id":1,"title":"The Stand","author":{id:1}}

If you wish for the author to be included as part of the rendering, there are two requirements, first you must make sure the association is initialized.

If the render method encounters a proxy, it will not traverse into the relationship to avoid N+1 query performance problems.

The same applies to one-to-many collection associations. If the association has not been initialized the render method will not traverse through the collection!

So you must make sure your query uses a join:

Book.findByTitle("The Stand", [fetch:[author:"join"]])

Secondly when calling the render method you should pass the deep argument:

json g.render(book, [deep:true])

Alternatively, to only expand a single association you can use the expand argument:

json g.render(book, [expand:['author']])

request parameters can also be used to expand associations (eg. ?expand=author), if you do not want to allow this, then use includes or excludes to include only the properties you want.

Finally, if you prefer to handle the rendering yourself you can do by excluding the property:

2.6.2. Altering the Response Headers

The HttpView trait defines a variety of methods for inspecting the request and altering the response.

The methods available are only a subset of the methods available via the HttpServletRequest and HttpServletResponse objects, this is by design as view logic should be limited and logic performed in the controller instead.

2.6.3. Accessing the Request

Various aspects of the request can be accessed by the request object defined by the HttpView trait:

json.person {
name "bob"
userAgent request.getHeader('User-Agent')
}

Parameters can be accessed via the params object which is an instance of Parameters:

In both cases the convention of the variable name matching the template name is used.

2.8. Content Negotiation

GSON views integrate with Grails' content negotiation infrastructure. For example if you create two views called grails-app/views/book/show/show.gsp (for HTML) and grails-app/views/book/show/show.gson (for JSON), you can then define the following action:

grails-app/controllers/myapp/BookController.groovy

defshow() {
respond Book.get(params.id)
}

The result is that if you send a request to /book/show it will render show.gsp but if you send a request to /book/show.json it will render show.gson.

In addition, if the client sends a request with the Accept header containing application/json the show.gson view will be rendered.

2.8.1. Content Negotiation and Domain Classes

Content negotiation also works nicely with domain classes, for example if you want to define a template to render any instance of the Book domain class you can create a gson file that matches the class name.

For example given a class called demo.Book you can create grails-app/views/book/_book.gson and whenever respond is called with an instance of Book Grails will render _book.gson.

defshow() {
respond Book.get(params.id)
}

If you define an index action that responds with a list of books:

defindex() {
respond Book.list()
}

Then you can create a corresponding grails-app/views/book/index.gson file that renders each book:

grails-app/views/book/index.gson

@FieldList<Book> bookList
json tmpl.book(bookList)

When responding with a list of objects Grails automatically appends the suffix "List" to the model name, so in this case the model name is bookList

By calling the tmpl.book(..) method with the list of books the grails-app/views/book/_book.gson template will be rendered for each one and a JSON array returned.

2.8.2. Global Default Template

You can also define a /object/_object template that is rendered by default if no other template is found during content negotiation. To do this create a file called /grails-app/views/object/_object.gson where the name of the model is object, for example:

model {
Object object
}
json g.render(object)

2.8.3. Content Negotiation and Versioned APIs

A typical use case when building REST APIs is the requirement to support different versions of the API. GSON views can be versioned by including the version in the name of the view.

Grails will then use the ACCEPT_VERSION header when resolving the view.

For example given a view called /book/show.gson if you wish to deprecate your previous API and create a version 2.0 API, you can rename the previous view /book/show_v1.0.gson and create a new /book/show.gson representing the new version of the API.

Then when the client sends a request with the ACCEPT_VERSION header containing v1.0 the /book/show_v1.0.gson view will be rendered instead of /book/show.gson.

2.8.4. Content Negotiation and View Resolving Strategy

Grails takes into account a number of factors when attempting to resolve the view including the content type, version and locale.

2.9.1. Generating HAL Links

The above example uses the hal.links(..) method to render links for a domain resource and the hal.embedded(..) method to define any embedded objects that form part of the HAL response.

The hal.links(..) method will by default create a link to the resource, but you can define additional links by annotating the domain class with either grails.rest.Linkable or grails.rest.Resource and using the link method on the object:

book.link(rel:"publisher", href:"http://foo.com/books")

The link will then be included in the HAL output.

If you wish to be specific about which links to include you can do so by passing a map of objects to link to:

2.9.2. Rendering Domain Classes as HAL

If you prefer to let the plugin handle the rendering of your object you can use the hal.render(..) method:

model {
Book book
}
json hal.render(book)

The hal.render method works the same as the g.render method, accepting the same arguments, the difference being it will output HAL links for the object via hal.links and also output associations fetched via a join query for hal.embedded.

For example you can also customize the content of the generated HAL with a closure:

model {
Book book
}
json hal.render(book) {
pages 1000
}

2.9.3. Embedded Association and HAL

Generally, when using the hal.render(..) method, _embedded associations are only rendered for associations that have been initialized and eagerly fetched. This means that the following query will not render the book.authors association:

Book.get(params.id)

However, this query will render the book.authors association:

Book.get(params.id, [fetch:[authors:'eager']])

This is by design and to prevent unexpected performance degradation due to N+1 query loading. If you wish to force the render method to render _embedded associations for HAL you can do see using the deep argument:

json hal.render(book, [deep:true])

You can prevent HAL _embedded associations from being rendering using the embedded:false parameter:

model {
Book book
}
json hal.render(book, [embedded:false])

You can also render embedded associations without using the hal.render(..) method, by using the hal.embedded(..) method:

model {
Book book
}
json {
hal.embedded(book)
title book.title
}

Like the hal.links(..) method, the hal.embedded(..) method should come first, before any other attributes, in your JSON output

You can also control which associations should be embedded by using a map argument instead:

The trait provides a number of different render method implementations that can either render a JSON view found in grails-app/views or render an inline String. For example to render an inline template:

Links generated by json views in a unit test may not match what they would normally generate in the standard environment. To fully test links, use a functional test.

2.13. Plugin Support

Grails plugins as well as standard Gradle Java/Groovy projects can provide json views to your application.

2.13.1. Grails Plugins

Since JSON views are compiled all of a plugin’s views and templates are available for use in your applications.

The view resolution will look through all of the application’s configured plugins for views that match a particular name. By default, the views a plugin supplies should be stored in grails-app/views, just like applications.

2.13.2. Basic Libraries

The most common use case to provide views in a standard library is to provide global templates. Global templates are templates designed to render a given class as JSON. In order to provide views in a standard Gradle project, you should configure your own view compilation task.

Below is an example Gradle build that adds a compileViews task for templates located into src/main/gson:

Once this is in place any applications that includes this library will have access to the templates provided.

For example if you want to render all instances of type foo.bar.Birthday create a template called src/main/gson/foo/bar/_birthday.gson then compile the template and place the JAR on the classpath of your application.

See the GeoJSON templates for MongoDB for example of how it provides global templates for Mongo specific classes.

2.13.3. Customizing the Compile Task

Unless otherwise configured, the project name of the plugin (the gradle project.name) is used as the packageName when compiling JSON views.

In Gradle project.name is generally calculated from the current directory. That means that if there is mismatch between the current directory and your plugin name view resolution from a plugin could fail.

For example consider a plugin named FooBarGrailsPlugin, in this case Grails will search for views that match the plugin name foo-bar. However, if the directory where the plugin is located is called fooBar instead of foo-bar then view resolution will fail and the view will not be found when the plugin is installed into an application.

To resolve this issue you can customize the compileGsonViews task in the plugin’s build.gradle

compileGsonViews {
packageName = "foo-bar"
}

By setting the packageName property to correctly match the convention of the plugin named (FooBarGrailsPlugin maps to foo-bar) view resolution will succeed.

2.14. Configuration

JSON views configuration can be altered within grails-app/conf/application.yml. Any of the properties within the JsonViewConfiguration interface can be set. The json view configuration extends GenericViewConfiguration, therefore any properties in that interface can be set as well.

For example:

grails:
views:
json:
compileStatic: truecache: true...

Alternatively you can register a new JsonViewConfiguration bean using the bean name jsonViewConfiguration in grails-app/conf/resources.groovy.

The same settings in grails-app/conf/application.yml will also be used by the Gradle plugin for production compilation.

The Gradle plugin compiles views using a forked compiler. You can configure the forked compilation task in Gradle as follows:

The result is all JSON views have a response object that can be used to control the HTTP response:

response.header "Token", "foo"

The trait cannot be defined in the same project as you are compilation as it needs to be on the classpath of the project you are compiling. You will need to create a Grails plugin and use a multi-project build in this scenario.

2.14.3. Default Mime Types

The mime types that will be accepted by default for JSON view rendering is configurable.

2.15. IntelliJ Support

When opening .gson files in Intellij they should be opened with the regular Groovy editor.

The plugin includes an IntelliJ GDSL file that provides code completion when using IntelliJ IDEA.

Intellij GDSL is a way to describe the available methods and properties so that code completion is available to the developer when opening .gson files.

The GDSL file is generally kept up-to-date with the codebase, however if any variation is spotted please raise an issue.

2.16. Debugging Views

Generally views should be kept simple and if you arrive to the point where you need to debug a view you probably have too much logic in the view that would be better off handled by the controller prior to supplying the model.

Nevertheless here are a few view debugging tips to help identify problems in the view.

2.16.1. Introspecting the Model

Every JSON view is a Groovy script and like any Groovy script the model is defined in a binding variable. Therefore you can easily find out the model at any time by either logging or printing the binding variables:

model {
Book book
}
// use the log variable
log.debug "Model is $binding.variables"// use system outSystem.out.println "Model is $binding.variables"
json g.render(book)

If you using the log variable then you will need to enabling logging for the grails.views package in grails-app/conf/logback.groovy

2.16.2. Connecting a Debugger

Some IDEs like Intellij IDE allow you to set break points and step debug within the view itself. As mentioned previously you shouldn’t arrive to this point and if you do you have too much logic in your view, but if you do need it the feature is there.

3. Markup Views

3.1. Introduction

Markup Views are written in Groovy, end with the file extension gml and reside in the grails-app/views directory.

3.3. Markup View API

The MarkupViewTemplate superclass implements the MarkupView trait which in turn extends the the GrailsView trait.

Much of the API is shared between JSON and Markup views. However, one difference compared to JSON views is that you must use this as a prefix when refering to properties from the parent class. For example to generate links this will produce a compilation error: