Grails渲染JSON迷你手册

http://memo.feedlr.com/?p=6

Mini guide to rendering JSON with Grails

Grails has built-in support for a JSON building DSL, which, together with the render controller dynamic method, makes rendering JSON responses an enjoyable job. But there seems to be a lack of consolidated information in the official documentation to clarify all the bits about getting anything you want in JSON. So here’s my little mini guide to share with you.

Ground Rules
As the JSON spec reads,

JSON is built on two structures:

A collection of name/value pairs. In various languages, this is realized as an object, record, struct, dictionary, hash table, keyed list, or associative array.
An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.
So there are only two rules for rendering JSON with Grails.

1. To render a collection of name/value pairs (an object), use the method call syntax.

e.g.

render(contentType:'text/json'){
pair(name:'value')
}
Will be rendered as

{"pair":{"name":"value"}}
2. To render an array of values, use a closure

e.g.

render(contentType:'text/json'){
collection{
pair(name:'value')
pair(name:'value1')
}
}
Will be rendered as

{collection:[{"name":"value"},{"name":"value1"}]}
Anything inside the collection closure becomes the values of the array. And because of this, the method name “pair” has no place to appear in the rendered array.

Putting It to Work
Here’s an example putting the two rules together.

render(contentType:'text/json'){
studio(name:'Pixar',website:'pixar.com')
films{
film(title:'Toy Story',year:'1995')
film(title:'Monsters, Inc.',year:'2001')
film(title:'Finding Nemo',year:'2003')
}
}
The result JSON will be

{"studio":{"name":"Pixar","website":"pixar.com"},
"films":[
{"title":"Toy Story","year":"1995"},
{"title":"Monsters, Inc.","year":"2001"},
{"title":"Finding Nemo","year":"2003"}
]}
When DSL is Not Enough
But there’s a gotcha about the current JSON Builder DSL to keep in mind. As of 1.0 RC3, You can have objects inside an array, but not an array inside an object (except the JSON object itself).

For example, I want a JSON structure like this.

{"object":{"collection":[{"name":"value1"},{"name":"value2"}]}}
I may write something like this,

render(contentType:'text/json'){
object(collection{
item(name:'value1')
item(name:'value2')
})
}
Although it’s syntactically correct, you will get a runtime exception of

java.lang.IllegalArgumentException: JSON Builder: not implemented
at grails.util.JSonBuilder.createNode(JSonBuilder.java:121)
...
Oh, maybe I made a mistake, I think. The closure is not a key/value pair there. And I change the code like this

render(contentType:'text/json'){
object(collection:{
item(name:'value1')
item(name:'value2')
})
}
This looks better. Does it work? Nope. It’s not what you’d expect.

{"object":{"collection":"DummyController$_closure15_closure27_closure28@4af40c"}}
This can’t be right. Then how about

render(contentType:'text/json'){
object(collection:collection{
item(name:'value1')
item(name:'value2')
})
}
Oddly, this will be rendered as

{"collection":[{"name":"value1"},{"name":"value2"}],"object":{"collection":1}}
The Almighty JSON converter
So is there any workaround, when JSON Builder DSL fails you? Yes! You can always turn to the JSON converter. Construct a temporary Map to contain the data, and use the converter to render the map as a whole.

To correctly render the JSON data in the previous section, you can use the JSON converter this way. In your controller, use the following code.

import grails.converters.*
...
def jsonMethod = {
...
def json = [object:[collection:[[name:'value1'],[name:'value2']]]]
render json as JSON
}
The result JSON will be exactly what I want

{"object":{"collection":[{"name":"value1"},{"name":"value2"}]}}
You can render any object using the JSON converter. And the results are pretty predictable.

Performance-wise, I’m not sure if there’s any difference or anyone has done any benchmark yet. So I’d say that it’s really your call which way to make your JSON. The converter doesn’t look as sexy as the DSL, but it sure works great. I may just choose either one depending on my mood:)

Tags: dsl, Grails, javascript, json

This entry was posted on Sunday, January 13th, 2008 at 4:11 pm and is filed under Grails. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

Login
Add New Comment

Post as …
Showing 9 comments
Sort by      Subscribe by email    Subscribe by RSS

Suejo75 1 week ago
How can i read a Json file generated by grails? When i do this my application ask me if i want to open it or down load so anybody can help me?
Like  Reply

Merrick BeforeBuffaloDogFood 1 month ago
It is good, I think it is helpful.
Like  Reply

Horses For Sale 1 month ago
The "render as JSON" is an excellent workaround and allows for nice refactoring of business logic into services so controller methods simply invoke service calls and render the resulting map.
Like  Reply

verunchik 5 months ago
Hallo, can you help me to make this json text:
I need: {"Result":[{"assets":["aaa", "bbb"]},...
but I have: {"Result":[{"assets":"[aaa, bbb]"},...
My code is:
render(contentType:"text/json") {
Result {pair(assets: links)
}}
links is array ["aaa", "bbb"]
Thanks!
Like  Reply

Guest 8 months ago
Hi, nice to meet you, my site provide DSL service, i hope you can get some information interesting in my site
Like  Reply

Noels 9 months ago
Thanks for the excellent article. That has helped me a great deal. Do you happen to know how I can get an anonymous collection like this: [{"name":"value"},{"name":"value1"}] ?
Like  Reply

modestymaster 6 months ago in reply to Noels
Using the builder example above, you may want to consider something like this:

def fakeJSON = []
listOfStuff.each {
fakeJSON.add(["name":it.text()])
}
render fakeJSON as JSON

你可能感兴趣的:(JavaScript,json,grails,performance)