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
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.
but I need to know how to compose (decorate) existing
MessageBodyWriters
with a newMessageBodyWriter
for myStream
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
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.