Grails渲染JSON迷你手册

[url]http://memo.feedlr.com/?p=6[/url]

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值