I like Grails for many reasons - it has all of the advantages of
Rails plus the ability to make use of anything else running on the
JVM. This means it is easier for Java developers to move to Grails
and there is no need to re-implement working Java code. Pretty cool
if you ask me.
There is just on major problem with Grails - the JSON and XML
support is a terrible. The good news it that you can fix it...of
course it should be fixed in the framework but that is a different
issue....
JSON Data binding fix for Grails
Have you seen this error when binding JSON in Grails? Unable
to Marshall to a Domain Object - no matching editors or conversion
strategy found
Anyone that has tried to use JSON to communicate with a Grails
backend has quickly discovered that the native JSON implementation
does not support bindings for nested objects. They also discover
that it is difficult to customize the JSON generated for an object.
In my opinion, data binding and JSON customization are required
minimum functionality - especially when writing
Single-Page-Applicaitons (SPA's) backed by Grails.
The problem with the native JSON handling in Grails is really just
two defects (at least I call them defects)
The result from the native JSON Parser cannot be used to bind nested
objects
As we have discovered, data binding only works with Maps -
JSONElement implements Map, but JSONArray (representing nested
objects for binding) is not supported for data binding. The only
option you have is to convert the JSONArray to a map. In my case, I
looked at the Map that would result from a traditional form
submission and discovered that in order to create an identical map,
the conversion needed to create nested maps as wells as individual
(flat) keys representing the the data in the map - this means that
some data is duplicated in the resulting map (just like it is in a
traditional form submission) I only mention this so that you know it
is intentional when you start playing with the code. NB: In this
example, I use the Jackson parser, but I think the native JSON
parser also work with this code.
First, add the following dependacies to your BuildConfig.groovy
file.
If you are using the JAX-RS plugin, download the DomainObjectReader class and put it in your grails-app\providers directory and your
done. Otherwise, you can use this code to create a valid,
bindable parameters map from your JSON.
type is the class of the domain object
entityStream contains the JSON
charset is the character encoding of the JSON in the entityStream
As you work with Grials and JSON, you will eventually dislike
something about the JSON generated by Grails. You will do a little
searching on the web and discover that you can register custom
ObjectMarshaller's with the JSON parser - happy with a solution
you implement you ObjectMarshaller's despite that
nagging feeling that this type of thing really should be handled in
a more "data driven" way by Grails. All is well with your
top level classes, but no matter what you do, the objects on the
many side of the hasMany relationship always render the same JSON
despite having registered a custom ObjectMarshaller. You
look through the Grails source code and discover that
DomainClassMarshaller.asShortObject(...) ignored custom
ObjectMarshaller's - it turns out that you cannot customize
the JSON in all cases.
I wanted a solution that supported annotations to control how the
JSON was rendered, so I settled on an implementation that makes
DomainClassMarshaller aware of two Jackson annotations to
control how the JSON is rendered.
I started with the original DomainClassMarshaller and made changes
to marshallObject to look at the @JsonIgnore annotations at the
class level and property level to determine which properties to
ignore. I then made changes to asShortObject to always include any
property with the @JsonInclude annotation and to also omit the
"class" property if necessary.
I think this implementation is far from complete, but it should be
enough to get you started with your own customizations if necessary.
(please tell me about your changes)
To make use of this new Marshaller you must update Bootstrap.groovy
with the following: