Search code examples
jsongrailsone-to-manyprofile

Grails 3 profiles,JSON rendering and One to Many with self


I'm working with gson and web profile.

My domain is:

package json
import grails.rest.*

@Resource(readOnly = false, formats = ['json', 'xml'])
class Hero {
    String name
    String data
    String relation
    Book book
    static hasMany = [children: Hero]

My controller is:

package json

import grails.rest.*
import grails.converters.*

class HeroController extends RestfulController {
    static responseFormats = ['json', 'xml']
    HeroController() {
        super(Hero)
    }
    def show(Hero hero){
        respond hero
    }
}

My gson:

hero.gson

import json.Hero

model {
    Hero hero
}

json tmpl.hero(hero)

_hero.gson

import json.Hero

model {
    Hero hero
}

json {
    //data hero.data
    id hero.id
    data(relation: hero.relation)
    name hero.name
    children g.render(hero.children)
}

If I run it with restful profile then the all children nodes are rendered correctly. If I use web profile only two levels deep are rendered.

My expected result is:

{
    "id": 4,
    "data": {
        "relation": "e"
    },
    "name": "e",
    "children": [{
            "id": 2,
            "data": {
                "relation": "c"
            },
            "name": "c",
            "children": [{
                    "id": 1,
                    "data": {
                        "relation": "b"
                    },
                    "name": "b",
                    "children": []
                }
            ]
        }

Is it possible to do one to many json rendering (as with the restful profile)? Is there a way to control depth of the rendering?

P.S. I read the documentation and this part is not very clearfor me:

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])


Solution

  • yes it's possible to render your json using web profile. I can do it by adding to build.gradle:

    apply plugin:"org.grails.plugins.views-json"
    
    dependencies {
    
        compile "org.grails.plugins:views-json"
        compile "org.grails.plugins:views-json-templates"
    
    
    
    bootRun {
        jvmArgs('-Dspring.output.ansi.enabled=always')
        addResources = true
    }
    

    and using your code with a little change in the domain class: children lazy: false

    @Resource(readOnly = false, formats = ['json', 'xml'])
    class Hero {
        String name
        String data
        String relation
        //Book book
        static hasMany = [children: Hero]
    
        static mapping = {
            children lazy: false
        }
    

    Which will change the fetch mode. You can consult about fetching here:

    enter link description herehttp://docs.grails.org/3.1.1/ref/Database%20Mapping/fetch.html

    http://docs.grails.org/3.1.1/guide/single.html#fetching