Question

I have two domain classes one is parent and other one is child and i have a hasMany relationship between them. Parent class has many childs and child class belongs to parent class. And here is coding example.

class Parent{
   String name
    static hasMany = [childs:Child] 
    static constraints = {
   }
}


class Child{
   String name
   static belongsTo = [parent:Parent]
   static constraints={}
}

Problem is as soon as I get the parent object the child objects associated with parent class were also fetched. But when I convert the object to JSON I don't see the child object completely I can only able to see the ID's of child objects. I want to see all columns of child object instead of only Id.

Converted JSON response:

[{"class":"project.Parent","id":1,
  "name":"name1","childs":[{"class":"Child","id":1},{"class":"Review","id":2}]}]

But I want the response which contains name of child object too, as follows

[{"class":"project.Parent","id":1,"name":"name1",
  "childs":[{"class":"Child","id":1,"name":"childname1"},
            {"class":"Review","id":2,"name":"childname2"}
           ]
}]

Any help greatly appreciated. Thanks in advance.

Was it helpful?

Solution

The issue is with the use of default JSON converter. Here are your options:

 1. Default  -  all fields, shallow associations
    a. render blah as JSON

 2. Global deep converter - change all JSON converters to use deep association traversal
    a. grails.converters.json.default.deep = true

 3. Named config marshaller using provided or custom converters
    a. JSON.createNamedConfig('deep'){
        it.registerObjectMarshaller( new DeepDomainClassMarshaller(...) )
    }
    b. JSON.use('deep'){
        render blah as JSON
    }

 4. Custom Class specific closure marshaller 
    a. JSON.registerObjectMarshaller(MyClass){ return map of properties}
    b. render myClassInstance as JSON

 5. Custom controller based closure to generate a map of properties
    a. convert(object){
        return map of properties
    }
    b. render convert(blah) as JSON

You are currently using Option 1, which is default.

The simplest you can do is use Option 2 to set global deep converter, but be aware this effects ALL domain classes in your app. Which means that if you have a large tree of associations culminating in a top level object and you try to convert a list of those top level objects the deep converter will execute all of the queries to fetch all of the associated objects and their associated objects in turn. - You could load an entire database in one shot :) Be careful.

OTHER TIPS

The latest grails automatically deep converts but you are probably a victim of lazy loading.

The children are not loaded at access and hence the JSON converter cannot convert them to JSON. The workaround is to put this

static mapping = { childs lazy: false }

user dbrin is correct, but there's one more option. You could also use the Grails GSON Plugin:

https://github.com/robfletcher/grails-gson#readme

The Plugin adds some more features when dealing with json data.

The suggested solution is working, however I had some trouble referencing "grailsApplication". It turns out, that you can ingest it like any other service. I put the following code into the

BootStrap.groovy

file. Also, the class DeepDomainClassMarshaller handles quite well bidirectional circular references, but beware that the JSON Payload is not to big after all deep deferencation.

package aisnhwr

import grails.converters.JSON
import grails.core.GrailsApplication
import org.grails.web.converters.marshaller.json.DeepDomainClassMarshaller

class BootStrap {

    GrailsApplication grailsApplication

    def init = { servletContext ->
        JSON.createNamedConfig('deep'){
            it.registerObjectMarshaller( new DeepDomainClassMarshaller(false, grailsApplication) )
        }
    }
    def destroy = {
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top