mehandih / grails-jaxrs

Automatically exported from code.google.com/p/grails-jaxrs
0 stars 0 forks source link

MessageBodyWriterSupport implementation does not affect GET of Collection resources #64

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
Grails app with failing test case is attached. To recreate from scratch:
1. grails create-app app
2. added jaxrs plugin and springframework.test dependency to BuildConfig.groovy
3. grails create-domain-class item
add Item.value String property
make generated ItemTests not fail
4. grails generate-resources app.Item
5. added provider ItemJsonWriter extends MessageBodyWriterSupport<Item>
6. added ItemIntegrationTest, which fails

What is the expected output? What do you see instead?

A GET to the Collection resource produces JSON without the customization 
defined by the MessageBodyWriterSupport implementation.

What version of the product are you using? On what operating system?
grails 2.0.3
jaxrs plugin 0.6

Original issue reported on code.google.com by ray.a.co...@gmail.com on 11 Jun 2012 at 4:51

Attachments:

GoogleCodeExporter commented 8 years ago
Hi Ray, thanks for providing a failing test. I'll take a look as soon as I can 
(within the next days) - too busy with other stuff at the moment, sorry. 
Cheers, Martin

Original comment by krass...@googlemail.com on 12 Jun 2012 at 6:01

GoogleCodeExporter commented 8 years ago
For rendering item collections you need to implement 
MessageBodyWriterSupport<Collection<Item>>:

@Provider
@Produces( 'application/json' )
class ItemsJsonWriter extends MessageBodyWriterSupport<Collection<Item>> {

    @Override
    protected void writeTo( Collection<Item> items, ... ) {
        // ...
    }
}

Alternatively, you can decide at runtime what to render by implementing 
MessageBodyWriter<Object> and let the isWriteable method decide if the writer 
is applicable or not. This technique, for example, is used for the grails-jaxrs 
DomainObjectWriter: 
https://github.com/krasserm/grails-jaxrs/blob/jaxrs-0.6/src/groovy/org/grails/ja
xrs/support/DomainObjectWriterSupport.groovy#L84

Hope that helps.

Cheers,
Martin

Original comment by krass...@googlemail.com on 17 Jun 2012 at 8:10

GoogleCodeExporter commented 8 years ago
That worked, after I finally figured out how to render a top-level array with 
JSONBuilder directly. It ended up looking like this:

    ....
    def converter = new JSONBuilder().build {
        array {
            items.each { item ->
                ignored( id : item.id,
                         value : "foo-$item.value" )
            }
        }
    }
    ....

If anyone figures out a better way to do that, please post a comment here. 
Feels not-quite-right to have to resort to a Map for the item props instead of 
the idiomatic "prop=value" provided by the JSONBuilder.

This is also unfortunate because it is so different from the JSONBuilder for 
rendering a single item (the other MessageBodyWriterSupport implementation) 
that the code can't be reused. This example is absurdly simple, but my real 
world use case is not.

I tried, and failed, to use the documented "element" method for this.

Original comment by ray.a.co...@gmail.com on 18 Jun 2012 at 6:47

GoogleCodeExporter commented 8 years ago
Maybe http://jersey.java.net/nonav/documentation/latest/json.html is 
interesting for you. As a side-note, I used this in the Scala world to generate 
both XML and JSON APIs from a single metamodel (JAXB-annotations) - 
http://krasserm.blogspot.de/2012/02/using-jaxb-for-xml-and-json-apis-in.html. 
The same idea is applicable to any Java/Groovy code as well.

Original comment by krass...@googlemail.com on 19 Jun 2012 at 5:41

GoogleCodeExporter commented 8 years ago
I ended up not taking either approach, but instead registered JSON and XML 
marshallers in BootStrap. The reasons are:
1) the code only appears once (single-Item marshaller also handles 
Collection<Item>), and
2) the code itself is much simpler, because it's not dealing with I/O at the 
stream level.

For this test app, it looks something like this:
    JSON.registerObjectMarshaller( Item ) {
        [ id : it.id,
          value : "foo-$item.value" ]
    }

The only drawback I see is that Item->JSON/XML code is in one place, while 
JSON/XML->Item code is in a completely different place.

Original comment by ray.a.co...@gmail.com on 19 Jun 2012 at 3:23