Source

JSONExclusionMarshaller / JsonExclusionMarshallerGrailsPlugin.groovy

Full commit
Burt Beckwith 9a89a59 
Jason Stonebrake… 956d88f 














Burt Beckwith 9a89a59 
Burt Beckwith aa94f71 
Jason Stonebrake… 107dc26 
Burt Beckwith 7c86973 
Jason Stonebrake… b6cae73 
Burt Beckwith 9a89a59 
Jason Stonebrake… b6cae73 

Burt Beckwith 9a89a59 
Jason Stonebrake… b6cae73 

Burt Beckwith 9a89a59 
Jason Stonebrake… b6cae73 
Jason Stonebrake… 020bc1a 
Jason Stonebrake… b6cae73 
Burt Beckwith 9a89a59 


Jason Stonebrake… b6cae73 




Burt Beckwith 9a89a59 
Jason Stonebrake… b6cae73 
Burt Beckwith 9a89a59 
Jason Stonebrake… b6cae73 
Burt Beckwith 9a89a59 


Jason Stonebrake… b6cae73 
Burt Beckwith 9a89a59 



Jason Stonebrake… b6cae73 
Burt Beckwith 9a89a59 

Jason Stonebrake… 956d88f 
Burt Beckwith 9a89a59 
Jason Stonebrake… b6cae73 
Jason Stonebrake… 956d88f 




Jason Stonebrake… b6cae73 
Jason Stonebrake… 956d88f 





Jason Stonebrake… 107dc26 
Jason Stonebrake… 956d88f 















Jason Stonebrake… b6cae73 
Burt Beckwith 9a89a59 
Jason Stonebrake… 3459f48 
Burt Beckwith 9a89a59 
Jason Stonebrake… 956d88f 
Burt Beckwith 9a89a59 
Jason Stonebrake… b6cae73 

Burt Beckwith f9fb69b 
Jason Stonebrake… b6cae73 
Burt Beckwith f9fb69b 
Jason Stonebrake… b6cae73 
Burt Beckwith f9fb69b 
Jason Stonebrake… b6cae73 
Jason Stonebrake… 956d88f 











Jason Stonebrake… b6cae73 
import grails.converters.JSON
import groovy.lang.GroovyObject
import groovy.lang.GroovyClassLoader
import groovy.util.ConfigSlurper
import groovy.util.ConfigObject

import java.beans.PropertyDescriptor
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Modifier

import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException
import org.codehaus.groovy.grails.web.converters.marshaller.ObjectMarshaller
import org.codehaus.groovy.grails.web.json.JSONWriter
import org.springframework.beans.BeanUtils


class JsonExclusionMarshallerGrailsPlugin {
    def version = "0.3"
    def grailsVersion = "2.0 > *"
    def pluginExcludes = [
        "grails-app/domain/**"
    ]

    def title = "JSON Exclusion Marshaller Plugin"
    def author = "Jason Stonebraker"
    def authorEmail = "jasonstonebraker@gmail.com"
    def description = '''Adds a convenience method to the Grails JSON converter for excluding properties from JSON output. Example use: excludeForPublicAPI(MyDomainClass, ['id', 'class']).'''

    def documentation = "https://bitbucket.org/stonebraker/jsonexclusionmarshaller/wiki/Home"

    def license = "APACHE"
    def issueManagement = [ system: "Bitbucket", url: "https://bitbucket.org/stonebraker/jsonexclusionmarshaller/issues" ]
    def scm = [ url: "https://bitbucket.org/stonebraker/jsonexclusionmarshaller/src" ]

    def doWithDynamicMethods = { ctx ->
        // TODO Implement registering dynamic methods to classes (optional)
        // println "Do with dynamic methods called..."

        JSON.metaClass.static.methodMissing = { String name, args ->
            def jsonConverter = delegate
            boolean isAnExclusionaryMethod = name.startsWith('excludeFor')

            if (!isAnExclusionaryMethod) {
                throw new MissingMethodException(name, JSON, args)
            }

            def impl = { Object[] vargs ->
                String configName = name // The name of the missing method called
                def type = vargs[0]
                def propertiesToExclude = vargs[1]

                jsonConverter.createNamedConfig(configName) {
                    it.registerObjectMarshaller(type) {
                        def o = it // Object being marshalled
                        def map = [:]

                        try {
                            PropertyDescriptor[] properties = BeanUtils.getPropertyDescriptors(o.getClass())
                            for (PropertyDescriptor property : properties) {
                                String propertyName = property.getName()
                                Method readMethod = property.getReadMethod()

                                if (readMethod != null
                                    && !(propertyName.equals("metaClass"))) {
                                    Object value = readMethod.invoke(o, (Object[]) null)
                                    map[propertyName] = value
                                }
                            }
                            
                            Field[] fields = o.getClass().getDeclaredFields()
                            for (Field field : fields) {
                                int modifiers = field.getModifiers()
                                if (Modifier.isPublic(modifiers)
                                    && !(Modifier.isStatic(modifiers)
                                        || Modifier.isTransient(modifiers))) {
                                    map[field.getName()] = field.get(o)
                                }
                            }

                        } catch (ConverterException ce) {
                            throw ce
                        } catch (Exception e) {
                            throw new ConverterException(
                                "Error converting Bean with class "
                                + o.getClass().getName(), e)
                        }

                        propertiesToExclude += this.getConfiguredGlobalExclusions(application)
                        propertiesToExclude.each { property -> map.remove(property) }
                        
                        return map
                    }
                }
            }

            JSON.metaClass."$name" = impl //future calls will use this

            return impl(args)
        }
    }

    private List getConfiguredGlobalExclusions(application) {
        GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader())
        ConfigObject config
        try {
           config = new ConfigSlurper().parse(classLoader.loadClass('JsonExclusionMarshallerDefaultConfig'))
        } catch (Exception e) {/*??handle or what? use default here?*/}
        
        def configGlobalExclusions = config.merge(application.config).jsonExclusionMarshaller.globalExclusions

        return (configGlobalExclusions) ? configGlobalExclusions.split(/,\s*/).collect { it } : []
    }
}