Search code examples
jsongrailsdata-binding

Binding JSON request data to command objects


In a Grails 2.5.X app, I can directly bind request data to a command object field of type Map like so:

class MyController {
  def myAction(Command command) {
    Map requestData = command.data
  }
}

class Command {
  Map data
}

It seems that internally Grails uses Gson for the parsing of the JSON data. If for example, the request data is {"page": 6} the corresponding Map will be

[page: new LazilyParsedNumber(6)]

i.e. the value stored in the Map is an instance of com.google.gson.internal.LazilyParsedNumber.

This is problematic for me. I would prefer if the Map were equivalent to that which would be created by:

new groovy.json.JsonSlurper().parseText('{"page": 6}')

which is:

[page: new Integer(6)]

I've looked into the various options for customising the databinding, and none of them hook into the pipeline sufficiently early. In other words, no matter which of the options I choose, the request data has already been processed by Gson.

Is it possible to replace Gson with JsonSlurper as the default parser of JSON request data?


Solution

  • Is it possible to replace Gson with JsonSlurper as the default parser of JSON request data?

    Yes, but it isn't something that we document or provide any particular support hooks for.

    The default JSON data binding source creator is at https://github.com/grails/grails-core/blob/v2.5.6/grails-web-databinding/src/main/groovy/org/codehaus/groovy/grails/web/binding/bindingsource/JsonDataBindingSourceCreator.groovy.

    An instance of that class is added to the Spring application context by the data binding plugin at https://github.com/grails/grails-core/blob/bd7cc10e17d34f20cedce979724f0e3bacd4cdb4/grails-plugin-databinding/src/main/groovy/org/codehaus/groovy/grails/plugins/databinding/DataBindingGrailsPlugin.groovy#L97.

    One thing you could do is write your own class which extends AbstractRequestBodyDataBindingSourceCreator (or just implements DataBindingSourceCreator) and register an instance of that class as a bean named jsonDataBindingSourceCreator and that will replace the default one with yours. Then you are on your own to use whatever techniques you like for parsing the body of the request and creating a Map.