How to stream JSON result with Jackson in Vert.x (java) How to stream JSON result with Jackson in Vert.x (java) json json

How to stream JSON result with Jackson in Vert.x (java)


I assume you are generating huge JSON documents as for the small ones string output is good enough: objectMapper.writeValue(<String>, result);

There's a problem with streams. ObjectMapper doesn't know the result size and you will end up with the exception:

java.lang.IllegalStateException: You must set the Content-Length header to be the total size of the message body BEFORE sending any data if you are not using HTTP chunked encoding.        at org.vertx.java.core.http.impl.DefaultHttpServerResponse.write(DefaultHttpServerResponse.java:474)

So in your example I would use temporary files for JSON output and then flush them into response (I haven't tested the code)

File tmpFile = File.createTempFile("tmp", ".json");mapper.writeValue(tmpFile, result);req.response().sendFile(tmpFile.getAbsolutePath(), (result) -> tmpFile.delete());

In case you know content length initially you can use the following code to map OutputStream with WriteStream

import org.vertx.java.core.buffer.Buffer;import org.vertx.java.core.streams.WriteStream;import java.io.IOException;import java.io.OutputStream;public class OutputWriterStream extends OutputStream {    public WriteStream writeStream;    public Runnable closeHandler;    @Override    public void write(int b) throws IOException {        throw new UnsupportedOperationException();    }    @Override    public void write(byte[] b, int off, int len) throws IOException {        if (off == 0 && len == b.length) {            writeStream.write(new Buffer(b));            return;        }        byte[] bytes = new byte[len];        System.arraycopy(b, off, bytes, 0, len);        writeStream.write(new Buffer(bytes));    }    @Override    public void write(byte[] b) throws IOException {        writeStream.write(new Buffer(b));    }    @Override    public void close() throws IOException {        closeHandler.run();    }}


This might be a bit better (and updated for Vertx3) answer:

import io.vertx.core.file.AsyncFile;import io.vertx.core.buffer.Buffer;import io.vertx.core.http.HttpServerResponse;import io.vertx.core.streams.WriteStream;import java.io.IOException;import java.io.OutputStream;public class OutputWriterStream extends OutputStream {    public OutputWriterStream(final WriteStream response) {        this.response = response;        this.buffer = new byte[8192];    }    @Override    public synchronized void write(final int b) throws IOException {        buffer[counter++] = (byte) b;        if (counter >= buffer.length) {            flush();        }    }    @Override    public void flush() throws IOException {        super.flush();        if (counter > 0) {            byte[] remaining = buffer;            if (counter < buffer.length) {                remaining = new byte[counter];                System.arraycopy(buffer, 0, remaining, 0, counter);            }            response.write(Buffer.buffer(remaining));            counter = 0;        }    }    @Override    public void close() throws IOException {        flush();        super.close();        if (response instanceof HttpServerResponse) {            try {                response.end();            }            catch (final IllegalStateException ignore) {            }        }        else if (response instanceof AsyncFile) {            ((AsyncFile) response).close();        }    }    private final WriteStream<Buffer> response;    private final byte[] buffer;    private int counter = 0;}