diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java index 71162eb49bb..1bebaf72227 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java @@ -45,14 +45,16 @@ import org.springframework.util.Assert; import org.springframework.util.MimeType; /** - * Base class providing support methods for Jackson 2.9 decoding. + * Abstract base class for Jackson JSON 2.9 decoding. * * @author Sebastien Deleuze * @author Rossen Stoyanchev + * @author Arjen Poutsma * @since 5.0 */ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport implements HttpMessageDecoder { + /** * Constructor with a Jackson {@link ObjectMapper} to use. */ @@ -60,6 +62,7 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple super(mapper, mimeTypes); } + @Override public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) { JavaType javaType = objectMapper().getTypeFactory().constructType(elementType.getType()); @@ -87,22 +90,17 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple private Flux tokenize(Publisher input, boolean tokenizeArrayElements) { try { JsonFactory factory = objectMapper().getFactory(); - JsonParser nonBlockingParser = factory.createNonBlockingByteArrayParser(); - Jackson2Tokenizer tokenizer = new Jackson2Tokenizer(nonBlockingParser, - tokenizeArrayElements); - return Flux.from(input) - .flatMap(tokenizer) - .doFinally(t -> tokenizer.endOfInput()); + JsonParser parser = factory.createNonBlockingByteArrayParser(); + Jackson2Tokenizer tokenizer = new Jackson2Tokenizer(parser, tokenizeArrayElements); + return Flux.from(input).flatMap(tokenizer).doFinally(t -> tokenizer.endOfInput()); } catch (IOException ex) { return Flux.error(new UncheckedIOException(ex)); } - } - private Flux decodeInternal(Flux tokens, - ResolvableType elementType, @Nullable MimeType mimeType, - @Nullable Map hints) { + private Flux decodeInternal(Flux tokens, ResolvableType elementType, + @Nullable MimeType mimeType, @Nullable Map hints) { Assert.notNull(tokens, "'tokens' must not be null"); Assert.notNull(elementType, "'elementType' must not be null"); diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2Tokenizer.java b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2Tokenizer.java index 2c6657b738b..dea07d0be52 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2Tokenizer.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2Tokenizer.java @@ -34,8 +34,9 @@ import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.util.Assert; /** - * Function that transforms an arbitrary split byte stream representing JSON objects into a - * {@code Flux}, where each token buffer is a well-formed JSON object. + * {@link Function} to transform a JSON stream of arbitrary size, byte array + * chunks into a {@code Flux} where each token buffer is a + * well-formed JSON object. * * @author Arjen Poutsma * @since 5.0 @@ -53,14 +54,16 @@ class Jackson2Tokenizer implements Function> { private int arrayDepth; // TODO: change to ByteBufferFeeder when supported by Jackson - private ByteArrayFeeder inputFeeder; + private final ByteArrayFeeder inputFeeder; + /** * Create a new instance of the {@code Jackson2Tokenizer}. * @param parser the non-blocking parser, obtained via * {@link com.fasterxml.jackson.core.JsonFactory#createNonBlockingByteArrayParser} - * @param tokenizeArrayElements if {@code true} and the "top level" JSON object is an array, - * each of its elements is returned individually and immediately after it was fully received + * @param tokenizeArrayElements if {@code true} and the "top level" JSON + * object is an array, each element is returned individually, immediately + * after it is received. */ public Jackson2Tokenizer(JsonParser parser, boolean tokenizeArrayElements) { Assert.notNull(parser, "'parser' must not be null"); @@ -71,6 +74,7 @@ class Jackson2Tokenizer implements Function> { this.inputFeeder = (ByteArrayFeeder) this.parser.getNonBlockingInputFeeder(); } + @Override public Flux apply(DataBuffer dataBuffer) { byte[] bytes = new byte[dataBuffer.readableByteCount()]; @@ -86,7 +90,7 @@ class Jackson2Tokenizer implements Function> { if (token == JsonToken.NOT_AVAILABLE) { break; } - calculateDepth(token); + updateDepth(token); if (!this.tokenizeArrayElements) { processTokenNormal(token, result); @@ -106,11 +110,7 @@ class Jackson2Tokenizer implements Function> { } } - public void endOfInput() { - this.inputFeeder.endOfInput(); - } - - private void calculateDepth(JsonToken token) { + private void updateDepth(JsonToken token) { switch (token) { case START_OBJECT: this.objectDepth++; @@ -149,7 +149,10 @@ class Jackson2Tokenizer implements Function> { result.add(this.tokenBuffer); this.tokenBuffer = new TokenBuffer(this.parser); } + } + public void endOfInput() { + this.inputFeeder.endOfInput(); } }