From 2835424f9d65aec0f47ed8d00cf8fdbaf9583d1d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 9 Apr 2019 17:59:10 +0200 Subject: [PATCH] Jackson2Tokenizer creates fully configured DeserializationContext Closes gh-22510 --- .../codec/json/AbstractJackson2Decoder.java | 4 ++-- .../http/codec/json/Jackson2Tokenizer.java | 12 ++++++++++-- .../codec/json/Jackson2TokenizerTests.java | 19 +++++++------------ 3 files changed, 19 insertions(+), 16 deletions(-) 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 7cf4c885fbe..b42e988d54d 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 @@ -87,7 +87,7 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple @Nullable MimeType mimeType, @Nullable Map hints) { Flux tokens = Jackson2Tokenizer.tokenize( - Flux.from(input), this.jsonFactory, getObjectMapper().getDeserializationContext(), true); + Flux.from(input), this.jsonFactory, getObjectMapper(), true); return decodeInternal(tokens, elementType, mimeType, hints); } @@ -96,7 +96,7 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple @Nullable MimeType mimeType, @Nullable Map hints) { Flux tokens = Jackson2Tokenizer.tokenize( - Flux.from(input), this.jsonFactory, getObjectMapper().getDeserializationContext(), false); + Flux.from(input), this.jsonFactory, getObjectMapper(), false); return decodeInternal(tokens, elementType, mimeType, hints).singleOrEmpty(); } 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 259a844c3c9..8549dee0bd9 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 @@ -27,6 +27,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.async.ByteArrayFeeder; import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext; import com.fasterxml.jackson.databind.util.TokenBuffer; import reactor.core.publisher.Flux; @@ -174,16 +176,22 @@ final class Jackson2Tokenizer { * Tokenize the given {@code Flux} into {@code Flux}. * @param dataBuffers the source data buffers * @param jsonFactory the factory to use + * @param objectMapper the current mapper instance * @param tokenizeArrayElements if {@code true} and the "top level" JSON object is * an array, each element is returned individually immediately after it is received * @return the resulting token buffers */ public static Flux tokenize(Flux dataBuffers, JsonFactory jsonFactory, - DeserializationContext deserializationContext, boolean tokenizeArrayElements) { + ObjectMapper objectMapper, boolean tokenizeArrayElements) { try { JsonParser parser = jsonFactory.createNonBlockingByteArrayParser(); - Jackson2Tokenizer tokenizer = new Jackson2Tokenizer(parser, deserializationContext, tokenizeArrayElements); + DeserializationContext context = objectMapper.getDeserializationContext(); + if (context instanceof DefaultDeserializationContext) { + context = ((DefaultDeserializationContext) context).createInstance( + objectMapper.getDeserializationConfig(), parser, objectMapper.getInjectableValues()); + } + Jackson2Tokenizer tokenizer = new Jackson2Tokenizer(parser, context, tokenizeArrayElements); return dataBuffers.flatMap(tokenizer::tokenize, Flux::error, tokenizer::endOfInput); } catch (IOException ex) { diff --git a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2TokenizerTests.java b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2TokenizerTests.java index f7b78ead02a..6e9ef255691 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2TokenizerTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2TokenizerTests.java @@ -184,22 +184,18 @@ public class Jackson2TokenizerTests extends AbstractLeakCheckingTestCase { @Test public void errorInStream() { DataBuffer buffer = stringBuffer("{\"id\":1,\"name\":"); - Flux source = Flux.just(buffer) - .concatWith(Flux.error(new RuntimeException())); - - Flux result = Jackson2Tokenizer.tokenize( - source, this.jsonFactory, this.objectMapper.getDeserializationContext(), true); + Flux source = Flux.just(buffer).concatWith(Flux.error(new RuntimeException())); + Flux result = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, true); StepVerifier.create(result) .expectError(RuntimeException.class) .verify(); } - @Test // SPR-16521 + @Test // SPR-16521 public void jsonEOFExceptionIsWrappedAsDecodingError() { Flux source = Flux.just(stringBuffer("{\"status\": \"noClosingQuote}")); - Flux tokens = Jackson2Tokenizer.tokenize( - source, this.jsonFactory, this.objectMapper.getDeserializationContext(), false); + Flux tokens = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, false); StepVerifier.create(tokens) .expectError(DecodingException.class) @@ -208,12 +204,11 @@ public class Jackson2TokenizerTests extends AbstractLeakCheckingTestCase { private void testTokenize(List source, List expected, boolean tokenizeArrayElements) { - Flux tokenBufferFlux = Jackson2Tokenizer.tokenize( + Flux tokens = Jackson2Tokenizer.tokenize( Flux.fromIterable(source).map(this::stringBuffer), - this.jsonFactory, this.objectMapper.getDeserializationContext(), - tokenizeArrayElements); + this.jsonFactory, this.objectMapper, tokenizeArrayElements); - Flux result = tokenBufferFlux + Flux result = tokens .map(tokenBuffer -> { try { TreeNode root = this.objectMapper.readTree(tokenBuffer.asParser());