JSON output of a view in Grails - json

Ok, I have a very simple app created in Grails.
I have a generated domain class (Person) and its generated controller, using the automatic Grails scaffold:
package contacts
class PersonController {
def scaffold = Person
}
Now I'd like to get a JSON representation of a Person object.
Do I have to change the view or the controller? And how?
Thank you.

Add the following to your controller:
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
def personList = Person.list(params)
withFormat {
html {
[personInstanceList: personList, personInstanceTotal: Person.count()]
}
json {
render personList as JSON
}
}
}
This should support both your scaffolding and the JSON output.
You can access the scaffolding as:
http://localhost:8080/contacts/person/list
You can access the Person list as json with:
http://localhost:8080/contacts/person/list?format=json
There are other ways to do it too, but I like doing it this way to leave the scaffolding around for testing.

Related

Grails 3 JSON Views custom rendering one-to-many associations

Okay I have tried everything I know to custom render a one-to-many association with JSON views and failed miserably.
Here's what part of my ticket domain looks like (All good here) ...
class TttTicket {
String title
String number
String description
TttUser assignee
String priority
String status
TttUser creator
Date dateCreated
Date lastUpdated
static mappedBy = [subscribers : 'none', creator:'none']
static belongsTo = [project:TttProject, creator:TttUser]
static hasMany = [subscribers: TttUser]
... blar blar
}
Here's my associated gson rendering template...
import ttt_api_server.TttTicket
model {
TttTicket tttTicket
}
json g.render(tttTicket, [excludes:['creator','subscribers','project']]){
creator {
id tttTicket.creator.id
name tttTicket.creator.name
email tttTicket.creator.email
}
project{
id tttTicket.project.id
name tttTicket.project.name
}
}
... which is working nicely so far. I want to now restricted the output of the properties for each of the subscribers.
How do I rotate over these? For example...
import ttt_api_server.TttTicket
model {
TttTicket tttTicket
}
json g.render(tttTicket, [excludes:['creator','subscribers','project']]){
creator {
id tttTicket.creator.id
name tttTicket.creator.name
email tttTicket.creator.email
}
subscibers g.render(){
tttTicket.subscribers.each {sub ->
return {
name sub.name
}
}
}
project{
id tttTicket.project.id
name tttTicket.project.name
}
}
This does not seem to be documented anywhere. I would like to controller the JSON out put of each subscriber at this level. Not at the domain level as I may need to change the property output depending on my JSON requirements.
Please help :-(
If I understand this correctly, when you do a REST call on ticket, you wanted the domain object that is inside the hasMany relationship to show a specified properties (in this case its subscribers). But when you call directly on the subscribers, you wanted all the properties.
You can create a map first and then render the objects based on the map created (have a look at this).
In your case:
import ttt_api_server.TttTicket
model {
TttTicket tttTicket
}
json g.render(tttTicket, [excludes:['creator','subscribers','project']]){
creator {
id tttTicket.creator.id
name tttTicket.creator.name
email tttTicket.creator.email
}
def s = tttTicket.subsribers.collect {
[
id: it.id,
name: it.name
]
}
subscribers s //this part is doing the rendering
project{
id tttTicket.project.id
name tttTicket.project.name
}
}
If this is the way you want this object to be rendered whenever it's rendered as JSON, I suggest implementing ObjectMarshaller<JSON>:
class TttTicketMarshaller implements ObjectMarshaller<JSON> {
#Override
boolean supports(Object object) {
return object instanceof TttTicket
}
#Override
void marshalObject(Object object, JSON converter) throws ConverterException {
TttTicket ticket = (TttTicket)object
def jsonWriter = converter.writer
jsonWriter.object()
jsonWriter.key("title")
jsonWriter.value(ticket.title)
//other fields
jsonWriter.key("creator")
jsonWriter.object()
jsonWriter.key("id")
jsonWriter.value(ticket.creator.id)
//other creator fields
jsonWriter.endObject()
}
}
I'm not 100% sure about the object() and endObject() functionality, but that appears to be the correct use.
Once you have this, register this in your BootStrap.groovy:
def init = { servletContext ->
JSON.registerObjectMarshaller(new TttTicketMarshaller())
}
With all this in place, you can simply use
render myTicket as JSON
to return the JSON representation of the object as defined by your marshaller

How do I render plain object as JSON, XML in Grails?

I have a controller (RestfulController) code similar to the following:
def doSomethingAwesome() {
Domain domainObject = prepareDomainObject()
//do something on the domain object
Model model = new Model(name: domainObject.name, description: domainObject.description)
respond model
}
As can be seen, I'm trying to respond with data wrapped inside a Model object. However, Model class is not a domain class; it's just a plain Groovy class. When I try to test this code in isolation, I get it to pass, but when I test it with all the other tests, I get GroovyCastException saying the object cannot be cast to JSON.
Some of the articles that came up of my searches suggest I put the model object inside a map then have that map get rendered. Something like:
render ['model': model] as JSON
However, this isn't how I'd like the response message to be. In addition, the XML message would look very different.
The Grails 2.4.3 demo project at https://github.com/jeffbrown/pogorespond demonstrates passing a POGO (not a domain class) to the respond method. https://github.com/jeffbrown/pogorespond/blob/1646c64fe2b37856fc87b64ae1cf5a6c4fd44cb1/grails-app/controllers/demo/DemoController.groovy contains the following:
package demo
class DemoController {
static responseFormats = ['json']
def index() {
def m = new Model(name: 'Some Name', description: 'Some Description')
respond m
}
}
class Model {
String name
String description
}

Exclude properties when rendering object as json in grails 2.4.0

I don't understand how can i exclude some properties when rendering an object as JSON in a controller (not a RESTController but a classic Controller).
I have this resources.groovy:
// Place your Spring DSL code here
import grails.rest.render.json.JsonRenderer
import com.appromocodes.Promocode
import com.appromocodes.ResponseStatus
import grails.rest.render.json.JsonCollectionRenderer
beans = {
responseStatusRenderer(JsonRenderer, ResponseStatus) {
excludes = ['enumType']
}
promocodeRenderer(JsonRenderer, Promocode) {
excludes = ['class', 'id', 'project']
}
}
Within my controller i tried in my action something like:
respond p as JSON
but this still gives me all the fields (also class, id and project fields).
What should i do?
The proper way to handle this is to register a custom JSON marshaller for your object. Begin by creating a new Marshaller in src/groovy/packageName/marshallers/PromocodeMarshaller.groovy with the following contents:
import packageName.Promocode
import grails.converters.JSON
class PromocodeMarshaller {
void register() {
JSON.registerObjectMarshaller(Promocode) { promocode ->
return [
id: promocode?.id,
// all the fields you'd like to return
// in your JSON object
]
}
}
}
Then, inside of your Bootstrap.groovy file, include the following:
import packageName.marshallers.PromocodeMarshaller
def promodcodeMarshaller = new PromocodeMarshaller()
promocodeMarshaller.register()
For a full description, see this article.

Reflectively save domain class instances in Grails

The problem is as follows: I want to handle a POST request with JSON body. The body consists of an array of JSON Objects, without further nesting, i.e. simple HashMaps. All of these objects represent JSON-serialized domain classes from an Android Application, which will have their counterpart in my Grails app. I am thinking of parsing the JSON body, iterating through every element and saving each node as its corresponding domain class instance.
a) How should I save the instance? I am quite new to Grails/Groovy so please excuse any huge mistakes. Code so far is
public static Object JSONArray2Instances(String json, Class type) {
def slurper = new JsonSlurper()
def result = slurper.parseText(json)
//we only want to parse JSON Arrays
if (!(result instanceof JSONArray))
return null
result.each {
def instance = it.asType(type)
// now I need to save to domain class!
}
}
b) where do I place the corresponding code? Currently it is in /grails-app/src/groovy. Where do the tests go? (Since it is not a 'real' Grails component)
c) Is an intermediate command object more appropriate?
Your code should go in to the controller which is handling the request. Please take a look at
gson-grails plugin which has examples of how to serialize and deserialze objects and map them to domain objects. Please take a look at the grails basics where they talk about the conventions used in the grails application and the layout. There are good examples at grails site. Hope this helps
I solved my problem as follows, based on help provided by the comment from allthenutsandbolts. : (Grails-Gson plugin was not needed)
Let N2696AdminAction be the name of a Domain Class
in my controller:
class N2696AdminActionController extends RestfulController{
static responseFormats = ['json', 'xml']
def JSONHandlerService
N2696AdminActionController() {
super(N2696AdminAction)
}
#Override
#Transactional
def save(){
if (request!=null)
JSONHandlerService.instancesfromJSON(request.JSON)
}
}
then I delegate persisting to my service as follows
class JSONHandlerService {
def instancesfromJSON(Object request){
//we only want to parse JSON Arrays
if (!(request instanceof JSONArray))
return null
request.each {
def domainClass = Class.forName("${it.type}",
true, Thread.currentThread().getContextClassLoader())
def newDomainObject = domainClass.newInstance(it)
newDomainObject.save(failOnError:true, flush:true, insert: true)
}
}
}
type is a Json attribute which holds the full (package inclusive) name for my class. This way, I can save to multiple Domain Classes with the same POST request.

Grails render encodeAsJSON GORM with ObjectId (mongodb)

Here is a Model
import org.bson.types.ObjectId
class Foo{
ObjectId id
String name
}
And here an action
def action = {
render(status:200, contentType:"application/json") {
['foo' : Foo.get(params.id)]
}
}
The action will return something like this
{"foo":{"class":"Foo","id":{"class":"org.bson.types.ObjectId","inc":340737392,"machine":-2019394572,"new":false,"time":1299107672000},"name":"fooName"]}
My question is, how can I send in the json the toString of the ObjectId, I don't want this
"id":{"class":"org.bson.types.ObjectId","inc":340737392,"machine":-2019394572,"new":false,"time":1299107672000}
I want something more like
"id":18893828183
I know I can select the parameters I want like:
def foo = Foo.get(params.id)
['foo' : 'Foo' :[id:foo.id.toString(), name:foo.name]]
But I don't want to declare always what I want to return as json, I want to return all the object, Foo.get(params.id).encodeAsJSON()...
Is there a way to override encodeAsJSON()
I already tried to add this
class Foo{
....
static transients : ['idStr']
def getIdStr(){
return this.id.toString()
}
....
}
But it's ignored in the encodeAsJSON()
I even tried this
class Foo{
....
def toJSON(){
def obj = this.encodeAsJSON()
def json = new JsonSlurper().parseText(obj);
json.idString = this.id.toString()
return json.toString()
}
...
}
this "works", but no....
because after this
render(status:200, contentType:"application/json") {
['foo' : Foo.get(params.id).toJSON()]
}
the render encode the json, so everything is "escaped"....
So what do you think is the solution, with a builder always defining what I want to return?
Hope, I made my question clear....
I'll start with the builder, hope you can give me another simpler / cleaner solution...
Thanks
edit
I just did a method that returns the object as a map so now I do something like this
render(status:200, contentType:"application/json") {
['foo' : getFooAsMap(Foo.get(params.id))]
}
Register this objectMarshaller at Bootstarp.groovy and it will work like a charm
import grails.converters.JSON
import org.bson.types.ObjectId
JSON.registerObjectMarshaller(ObjectId) {
return it.toStringMongod()
}
If you're going to be JSON-encoding your domain classes out to the web, I wonder if ObjectId might not be the best choice? The GORM/MongoDB integration allows you to use any type for the id. You could just declare it as a String type (which can be assigned as a toString of an ObjectId if you like to use that for its randomness) and then you don't need to worry about this mess. Any performance/scalability problems from this could be analysed/dealt with later, but I wouldn't expect there to be any unless it's a very large app.
Use GStrings in your map and you will get a numeric value for your ObjectId.
E.g.
render ["foo":"$foo.id"] as JSON
You can do this too:
def domainObj = YourDomainClass.get(params.id)
Map props = [:]
def domain = new DefaultGrailsDomainClass(YourDomainClass.class)
domain.properties.each{
props[it.name] = domainObj[it.name]
}
props["id"] = domainObj.id.toString()
render props as JSON
Or better yet, make it reusable. Put this closure someplace handy:
def mongoObjectResponse = {dobj ->
Map props = [:]
def domain = new DefaultGrailsDomainClass(YourDomainClass.class)
domain.properties.each{
props[it.name] = dobj[it.name]
}
props["id"] = dobj.id.toString()
// I like to leave room in my responses for messages and such
message = ""
obj = props
}
Then call like this from your controller:
return render(contentType: "text/json") {
mongoObjectResponse.delegate = delegate
mongoObjectResponse(domainObj)
}
Just define your domain class as
class Foo {
String id
String name
}
Instead of ObjectId