krasserm / grails-jaxrs

JAX-RS Plugin for Grails
http://code.google.com/p/grails-jaxrs/
Apache License 2.0
50 stars 48 forks source link

MessageBodyReaderSupport and MessageBodyWriterSupport for List<User> #39

Closed confile closed 10 years ago

confile commented 10 years ago

I have problems to generate the MessageBodyReaderSupport and MessageBodyWriterSupport for List and List

I have the following Resource

class UserResource {
@POST
    @Consumes('application/json')
    @Produces('application/json')
    List<User> getusers(List<UserDto>  userlist) { 
           return userlist
       }
}

I defined the MessageBodyWriterSupport for User and the MessageBodyReaderSupport for UserDto. I would expect that these reader and writer will be used when I use a collection typ like a List. But this is not the case I have to write my own reader and writer for

For example this:

@Provider
@Produces('application/json')
class ArrayListWriter extends MessageBodyWriterSupport<ArrayList> {
  ...
}

Can you please tell how I could write a MessageBodyReaderSupport and MessageBodyWriterSupport for a generic list class?

When I have MessageBodyWriterSupport for User and Item then it would be great when these writers are used when I generate List and List Is this possible? Would be great if you could post an example for that. Thank you for your help.

confile commented 10 years ago

@davidecavestro Can you please give me some help.

davidecavestro commented 10 years ago

I could be wrong, but I think your problem is related to type erasure applied by java compiler: Jersey has no ways to know that your list contains UserDto/User instances. I guess using a collection wrapper could do the trick, something like

class UserResource {
    @POST
    @Consumes('application/json')
    @Produces('application/json')
    UserCollection getusers(UserDtoCollection  userlist) { 
        return //some way to transform UserDtos in Users
    }
}

where the wrapper is as follows

public class UserDtoCollection {
    private List<UserDto> users;

    public UserDtoCollection() {
        users= new ArrayList<UserDto> ();
    }

    public UserDtoCollection(List<UserDto> users) {
        this.users = users;
    }

    public List<UserDto> getUsers() {
        return users;
    }

    public void setUsers(List<UserDto> users) {
        this.users = users;
    }

    @Override
    public String toString() {
        String s = " { User collection ";
        s += "users=" + users + " ";
        s += " User collection} ";
        return s;
    }
}

for which you should provide appropriate ad-hoc reader and writer

@Provider
@Produces("application/json")
class UserDtoCollectionWriter extends MessageBodyWriterSupport<UserDtoCollection> {

    @Override
    public void writeTo(UserDtoCollection users, MultivaluedMap<String, String> httpHeaders,
            OutputStream entityStream) {
        def builder = new groovy.json.JsonBuilder()
        builder (users.users
        )
        def w = new PrintWriter (entityStream)
        w.write (builder.toString())
        w.flush()
    }
}

and so on. There could be easiest ways to achieve the same, but I am not aware of them. Another interesting way to gain better control on how your data gets (de)serialized (hence converting reader/writer to serializer/deserializer) is configuring a ContextResolver with the ObjectMapper for Jackson, see http://wiki.fasterxml.com/JacksonFAQJaxRs .

confile commented 10 years ago

Thanks for your answer. I use this kind of writer:

@Provider
@Produces('application/json')
class UserDtoWriter extends MessageBodyWriterSupport<UserDto> {

    void writeTo(UserDto entity, MultivaluedMap httpHeaders, OutputStream entityStream) {
        def writer = new OutputStreamWriter(entityStream)
        def converter = [
            result : entity] as JSON

        converter.render(writer)

    }

}

Which version do you recommend PrintWriter or OutputStreamWriter? Also do I have the set w.flush() ?

davidecavestro commented 10 years ago

Taking DomainObjectWriterSupport as example, it does the following

def writer = new OutputStreamWriter(entityStream, charset)
def converter = new JSON(t)
converter.render(writer)
confile commented 10 years ago

@davidecavestro Yes I know, but which variant do you recommend?

davidecavestro commented 10 years ago

I've never used PrintWriter in production code for JaxRs services... it slipped out of a quick (and dirty) snippet of mines. So personally I'd go for OutputStreamWriter... especially because it propagates exceptions, while PrintWriter (from my understandings) catches them, as per its javadoc

Methods in this class never throw I/O exceptions, although some of its constructors may. The client may inquire as to whether any errors have occurred by invoking checkError().

Cheers Davde

confile commented 10 years ago

@davidecavestro Great thank you. I think it would be helpful if you add your Collection example here to your documentation.