Wiki

Clone wiki

JSONExclusionMarshaller / Home

What is the JSON Exclusion Marshaller?

The JSON Exclusion Marshaller is a Grails plugin that allows you to easily exclude class properties from the JSON converter's output. It does this by injecting a convenience method, excludeFor*(...), into Grails' JSON converter class.

What problem does the plugin solve?

The grails.converters.JSON class is powerful. That power comes at the cost of some complexity though. This plugin utilizes Groovy's meta-programming features to simplify the common task of excluding arbitrary properties from the JSON payload at run-time. The most common use for this plugin will be to simply ensure that the 'id' and 'class' properties (added by GORM and GroovyObject respectively) are not included in the JSON string. The plugin allows you to tackle this common task using expressive, domain specific language, quickly and simply. It also makes it trivial to specify at run-time which properties to exclude.

Installation

As a Dependency: compile ":json-exclusion-marshaller:0.3"

Via command line: grails install-plugin json-exclusion-marshaller

Method Signature

Class: grails.converters.JSON

public static excludeFor*(Class class, List<String> propertiesToExclude)

* This is the method that is injected by the JSON Exclusion Marshaller plugin.

Method Name

excludeFor*(...) is a synthesized method. There are no restrictions or conventions for what you put in the place of the * wildcard, though it must form a valid method name.

For instance, these are all valid:

import grails.converters.JSON

JSON.excludeForPublicAPI(...)
JSON.excludeForRESTClientsThatDontNeedAllOfThePropertiesThisObjectContains(...)
JSON.excludeForFoo(...)

As long as the method name you choose starts with "excludeFor", you're good to go.

Parameters:

  • class - The class of the object you will be excluding properties from.
  • propertiesToExclude - A list of the properties you want to be excluded from the JSON converter's output.

Usage

import grails.converters.JSON

// Given a TestStudent Domain Class
def student = new TestStudent([
    firstName: "Tobias",
    lastName: "Funke",
    gradePointAverage: 3.6,
    studentID: "FS-210-7312",
    socialSecurityNumber: "555-55-5555"
])

// When
// We create an excluder called 'excludeForTeachers'
JSON.excludeForTeachers(TestStudent, ['socialSecurityNumber', 'id', 'class'])

// Then
// We use the excluder we just created.
def json, resultTeachersWillSee

JSON.use('excludeForTeachers') {
    json = student as JSON
}
resultTeachersWillSee = json.toString()

println resultTeachersWillSee

Outputs:

{"firstName":"Tobias","gradePointAverage":3.6,"lastName":"Funke","studentID":"FS-210-7312"}

Walkthrough

First we instantiate a simple object called TestStudent. Nothing magical here...

student = new TestStudent([
    firstName: "Tobias",
    lastName: "Funke",
    gradePointAverage: 3.6,
    studentID: "FS-210-7312",
    socialSecurityNumber: "555-55-5555"
])

Next we make use of the excludeFor*() method. You can write anything that makes sense to your domain between "excludeFor" and "()". The whole method name is what you'll use next to marshall the object into the form you want.

We specify which properties a teacher is allowed to see by creating an "excluder" (don't look for documentation for a class named "Excluder". That's just what I call ObjectMarshallers that exclude properties). In our domain, teachers aren't allowed to see a students' social security numbers and the client developer has no need for GORM's id and class attributes.

JSON.excludeForTeachers(TestStudent, ['socialSecurityNumber', 'id', 'class'])

Finally, we use this excluder (ObjectMarshaller) to instantiate a JSON converter that will only output the properties appropriate for teachers to see.

def json, resultTeachersWillSee

JSON.use('excludeForTeachers') {
    json = student as JSON
}
resultTeachersWillSee = json.toString()

Do something with the result...

println resultTeachersWillSee

Outputs:

{"firstName":"Tobias","gradePointAverage":3.6,"lastName":"Funke","studentID":"FS-210-7312"}

Multiple Named Excluders

Create a bunch of excluders for different purposes/audiences.

// Create
JSON.excludeForTeachers(TestStudent, ['socialSecurityNumber', 'id', 'class'])
JSON.excludeForOtherStudents(TestStudent, ['gradePointAverage', 'studentID', 
    'socialSecurityNumber', 'id', 'class'])
JSON.excludeForTestAdministrators(TestStudent, ['gradePointAverage', 
    'socialSecurityNumber', 'id', 'class'])

// Use
JSON.use('excludeForTeachers') {
    json = student as JSON
}
resultTeachersWillSee = json.toString()

JSON.use('excludeForOtherStudents') {
    json = student as JSON
}
resultOtherStudentsWillSee = json.toString()

// We can also embed the excluder within use('deep') though it has no affect 
// our simple TestStudent class.
JSON.use('deep') {
    JSON.use('excludeForTestAdministrators') {
        json = new JSON(student)
        json.prettyPrint = false
        resultTestAdministratorsWillSee = json.toString()
    }
}

What's actually happening?

The excludeFor*() method creates a Named Configuration with a name of 'excludeForWhateverYouDecideToNameIt'. The Named Configuration then registers a custom ObjectMarshaller for the TestStudent domain class. The custom object marshaller is built into the excludesFor* sythesized method. It removes the properties we provided to it in the list. The JSON converter then outputs the desired JSON formatted string.

Configuring Global Exclusions

You can specify a list of properties that should always be excluded by the JSON converter by adding a line like this to your application's Config.groovy file:
jsonExclusionMarshaller.globalExclusions = "attached, errors, properties, version, class, id"

Other resources and discussion surrounding this issue

GRAILS-5791: "Create a way to easily exclude properties from Serialization when using the XML or JSON converters"

Official Grails Documentation: Using Named Configurations for Object Marshallers

StackOverflow: "grails.converters.JSON except few properties"

Rendering JSON in Grails: Part 3 Customise your JSON with Object Marshallers

Improvements in 0.3

  • Made the plugin useful for all pogos, not just Grails Domain Classes.
  • Added config element for global exclusions
  • Removed test code. Tests are now done externally.

Improvements to be made

  • Infer the class type of the target object rather than requiring it to be passed in. This is a challenge since the paradigm is based on static methods.
  • Stacking named marshallers to apply multiple marshallers to a single converter instance
  • Instance level ObjectMarshalling

Conclusion

This is my first grails plugin. I hope you find it useful. I'm three weeks new to Groovy and Grails, and those three weeks have been spent reading related books and staying productive at work. I welcome any suggestions/code contributions that would improve the plugin until Graeme and team are able to make updates to the JSON converter class to support this type of behavior.

Updated