Serialize Java 8 Stream with Jersey

后端 未结 2 822
無奈伤痛
無奈伤痛 2021-01-12 19:02

How can I serialize a Java 8 java.util.Stream with Jersey. I tried to write a MessageBodyWriter, but I need to know how to compose (decora

相关标签:
2条回答
  • 2021-01-12 19:36

    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.

    0 讨论(0)
  • 2021-01-12 19:37

    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.

    0 讨论(0)
提交回复
热议问题