Why? The reason is maybe:
1. You want to include some properties in JSON output which was not included in default, such as version. By the way, you can include version through three config option in grails new version. You can find more details on this issue at this or my last post.
2. You like to format some property yourself, such as date formatting
3. You want to output a object into a new JSON string, such as I want to output a relation property's toString() not its id.
4. ...
These days I was doing a RIA application using grails and ext. And, I met all the three requirements I said above. And here is what I got during this project:
1. To implement your marshaller, you can implement ObjectMarshaller or you can use closure when you register it. If you want to use closure, you can do this:
JSON.registerObjectMarshaller(Locale.class){
def rval= [:]
rval['ID']= it.toString()
rval['displayName']= it.getDisplayName()
return rval
}
For method 1, implementing ObjectMarshaller, You can check DomainClassMarshaller.java. But if what you want is just to output the return value of toString() of relation property, you really don't have to do it from the ground up. You can extend DomainClassMarshaller:
class AnotherDomainClassMarshaller extends DomainClassMarshaller{
protected void asShortObject(Object refObj, JSON json,
GrailsDomainClassProperty idProperty,
GrailsDomainClass referencedDomainClass) throws ConverterException {
JSONWriter writer = json.getWriter();
writer.object();
writer.key("class").value(referencedDomainClass.getName());
writer.key("id").value(extractValue(refObj, idProperty));
writer.key("value").value(refObj.toString());
writer.endObject();
}
}
2. After you create your marshaller, you should register it. And the best place in grails application is BootStrap.groovy:
JSON.registerObjectMarshaller(your marshaller instance)
3. If you want to register more than one marshaller, you should provide a priority. REMEMBER IT! Yes, it is a trick. If you ignore it, you will get a surprise: Your marshaller registered just now did not work! After I digged the source of grails, DefaultConverterConfiguration.java, I found the truth: Grails hold all the object marshallers in a treeset, which use priority as a comparator, if you register two marshallers with the same priority, grails will register your marshaller with default-priority(=0) if priority is missing, the second marshaller will not be registered. So my last version of BootStrap.groovy is:
But there is a problem I don't understand: the default ByteArrayMarshaller did not work, and I have to register it myself and provide a priority like this:
JSON.registerObjectMarshaller(...)
JSON.registerObjectMarshaller(..., 1)
JSON.registerObjectMarshaller(..., 2)
Who can tell me why?
JSON.registerObjectMarshaller(new org.codehaus.groovy.grails.web.converters.marshaller.json.ByteArrayMarshaller(), 1)
5 comments:
hi
the priority stuff is already fixed and will be in the next release of the Grails 1.2 branch. Regarding the ByteArrayMarshaller stuff, you might want to raise a JIRA issue (against the converters component) and I'll take a look at it asap.
Cheers, sigi
great! this stuff is really annoying. I just raised a jira issue about ByteArarrayMarshaller: http://jira.codehaus.org/browse/GRAILS-5143. thanks, sigi.
java.lang.NoSuchMethodError: org.codehaus.groovy.grails.web.converters.marshaller.json.DomainClassMarshaller: method ()V not found
Do you know why?
Hi,
I'm facing a strange issue in Grails 2.1.2. I registered a custom date marshaller for JSON converter. After my application starts within some time I see weird thing happens that at time Grails uses Default date marshaller and at times the custom date marshaller I registered.
I saw the priority note mentioned by you. Could it be related to that? I haven't used priority in my code
Thanks for pointing out the priority of registered marshallers. My problem was somehow different:
I have a domain class which inherits from another class. Both classes define properties and have their own JSON marshallers:
class A{
static marshalling={
JSON.registerObjectMarshaller(A) { A a ->
return [
p1: a.p1,
p2: a.p2
]
}
}
String p1
String p2
}
class B extends A{
static marshalling={
JSON.registerObjectMarshaller(B) { B b ->
return [
p1: b.p1,
p3: b.p3 ]
}
}
String p3
}
In my controller I did something like
..
render B.list() as JSON
..
but it always used the JSON marshaller definition of class A! So if you have a problem with inheritance and JSON marshaller (using extends in a class and custom JSON definition and inherit from a super class) you should use priorities!
So in my example I fixed the code to
JSON.registerObjectMarshaller(B,2) { B b ->
return [
p1: b.p1,
p3: b.p3
]
}
and everything is fine. Took me a few hours of my life-time, maybe some will save this time by reading about my grails JSON converter inheritance problem.
Best greetings
Post a Comment