Serialize Java 8 Stream with Jersey

拥有回忆 提交于 2019-12-01 05:14:26

but I need to know how to compose (decorate) existing MessageBodyWriters with a new MessageBodyWriter for my Stream

You can just inject Providers and use getMessagBodyWriter(...), passing in the required details to lookup the specific writer for that type. For example

@Provider
public class StreamBodyWriter implements MessageBodyWriter<Stream> {

    @Context
    private Providers providers;

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, 
            Annotation[] annotations, MediaType mediaType) {
        return Stream.class.isAssignableFrom(type);
    }

    @Override
    public long getSize(Stream stream, Class<?> type, Type genericType, 
            Annotation[] annotations, MediaType mediaType) { return -1; }

    @Override
    public void writeTo(Stream stream, Class<?> type, Type genericType, 
            Annotation[] annotations, MediaType mediaType, 
            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) 
            throws IOException, WebApplicationException {

        Object obj = stream.collect(Collectors.toList());
        Class<?> objType = obj.getClass();

        MessageBodyWriter writer = providers.getMessageBodyWriter(objType, 
                null, annotations, mediaType);

        writer.writeTo(obj, objType, null, annotations, 
                mediaType, httpHeaders, entityStream);

    }
}

If you look at the writeTo, first I call collect then get the returned type. Then lookup the writer for that type, then simply delegate to the writer.

Here is a test

@Path("stream")
public class StreamResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getStream() {
        List<Person> myList = Arrays.asList(
                              new Person("Stack"), 
                              new Person("Overflow"),
                              new Person("Sam"));
        Stream<Person> stream = myList.stream()
                                      .filter(p -> p.name.startsWith("S"));
        return Response.ok(stream).build();
    }

    public static class Person {
        public String name;
        public Person(String name) { this.name = name; }
        public Person() {}
    }
}

C:\>curl -v http://localhost:8080/api/stream
Result:
[{"name":"Stack"},{"name":"Sam"}]

As an aside, if you plan on manipulating the Stream in the writer, maybe look into using an Interceptor. Won't make a difference really, but if you want to stick to the Single Responsibility Principle, this is what the Interceptor is for, manipulating the request body.


Note: the above is standard JAX-RS

Alternatively...

Specifically with Jersey, you can also inject MessageBodyWorkers, for more specific lookup, and even calling its writeTo, which will delegate to the required writer, if one exsists.

In line with the purpose of use streams (without using stream.collect(Collectors.toList())), there's this interesting article showing how to serialize large data from a database.

It's something like this...

@GET
@Produces( "application/json" )
public Response streamGeneratedUuids() {

    return getNoCacheResponseBuilder( Response.Status.OK ).entity( new StreamingOutput() {

        @Override
        public void write( OutputStream os ) throws IOException, WebApplicationException {
            try (PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os))) ) {
                //iterate the java.util.stream and write to the OutputStream
                writer.print("....");       
            }
        }
    }).build();
}

It's not implemented with a MessageBodyWriter, but could adapted in my opinion.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!