Saturday, June 12, 2010

Return partial Domain Class

Reading the description of GRAILS-5791, I found this interesting question. The asker expects the JSON returned by "domain as JSON" not including some properties he lists.
There are serval solutions, the comments following that question have list two ones. Here I will give my own one. In my way, I will add one method to the domain classes in the grails app: part, which returns a map including or excluding some properties of a domain class. Then you can render the result map as JSON. The following shows how to use this method:
  def job= Job.get(1)

//include
render job.part(include:['categorys', 'source']) as JSON

//except
render job.part(except:['id', 'version']) as JSON
This sample is very intuitive. The first statement will result in a JSON only including categorys and source; the JSON rendered by second statement will include all the properties of the domain class except id and version.
Using part, you can make your life easier and also make your code more grace.
Now, let's see how to adding the part method to the domain classes:
  • how: In groovy, in order to add a method to an object dynamiclly, we can use mop.
  • when: In grail app, there is a file named BootStrap.groovy, where is a good place to do some initialization works.
When we know how and when to do that job, here is the detail:
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor as Events

class BootStrap {
def grailsApplication

def excludedProps = [Events.ONLOAD_EVENT,
Events.BEFORE_DELETE_EVENT, Events.AFTER_DELETE_EVENT,
Events.BEFORE_INSERT_EVENT, Events.AFTER_INSERT_EVENT,
Events.BEFORE_UPDATE_EVENT, Events.AFTER_UPDATE_EVENT]

def init = { servletContext ->
grailsApplication.domainClasses.each{ domainClass ->
domainClass.metaClass.part= { m ->
def map= [:]
if(m.'include'){
m.'include'.each{
map[it]= delegate."${it}"
}
}else if(m.'except'){
m.'except'.addAll excludedProps
def props= domainClass.persistentProperties.findAll {
!(it.name in m.'except')
}
props.each{
map[it.name]= delegate."${it.name}"
}
}
return map
}
}
}
def destroy = {
}
}
You also can change this to a plugin, then you can use this feature across different grails apps. Hope you enjoy this.

4 comments:

train of thought said...

oh my good, this is amazing thanks a lot, i was looking for something like this since i needed several json representations for example getShortJson() would get me a json with only 2 properties in my domain class, and then i would have getJSON which gets all the properties, with this method there is no need for the extra methods :)

צְבִיקַ'ה said...

awesome! this is so cool

nils said...

like magic :)

nils said...

i confirm that this here works too:

JSON.registerObjectMarshaller(Client) {
return it.part(except: ['unwantedClientFieldOrRelation'])
}