From 57067f58e1bcdd70506a6f2ada60eefabc107fda Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 23 Sep 2022 16:53:01 +0100 Subject: [PATCH] Polishing contribution Closes gh-28401 --- .../codec/json/AbstractJackson2Decoder.java | 134 +++++------- .../codec/json/AbstractJackson2Encoder.java | 205 ++++++++---------- .../AbstractJackson2HttpMessageConverter.java | 26 ++- .../CustomizedJackson2JsonDecoderTests.java | 18 +- .../CustomizedJackson2JsonEncoderTests.java | 23 +- 5 files changed, 175 insertions(+), 231 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 84818ce0cdc..0acce8ce5ce 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ import com.fasterxml.jackson.databind.util.TokenBuffer; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.util.context.ContextView; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; @@ -46,7 +47,6 @@ import org.springframework.core.log.LogFormatUtils; import org.springframework.http.codec.HttpMessageDecoder; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.MimeType; @@ -139,22 +139,26 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple Flux tokens = Jackson2Tokenizer.tokenize(processed, mapper.getFactory(), mapper, true, forceUseOfBigDecimal, getMaxInMemorySize()); - ObjectReader objectReader = getObjectReader(mapper, elementType, hints); - - return customizeReaderFromStream(objectReader, mimeType, elementType, hints) - .flatMapMany(reader -> tokens.handle((tokenBuffer, sink) -> { - try { - Object value = reader.readValue(tokenBuffer.asParser(mapper)); - logValue(value, hints); - if (value != null) { - sink.next(value); - } - } - catch (IOException ex) { - sink.error(processException(ex)); - } - }) - ); + return Flux.deferContextual(contextView -> { + + Map hintsToUse = contextView.isEmpty() ? hints : + Hints.merge(hints, ContextView.class.getName(), contextView); + + ObjectReader reader = createObjectReader(mapper, elementType, hintsToUse); + + return tokens.handle((tokenBuffer, sink) -> { + try { + Object value = reader.readValue(tokenBuffer.asParser(mapper)); + logValue(value, hints); + if (value != null) { + sink.next(value); + } + } + catch (IOException ex) { + sink.error(processException(ex)); + } + }); + }); } /** @@ -169,7 +173,7 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple * @since 5.1.14 */ protected Flux processInput(Publisher input, ResolvableType elementType, - @Nullable MimeType mimeType, @Nullable Map hints) { + @Nullable MimeType mimeType, @Nullable Map hints) { return Flux.from(input); } @@ -177,37 +181,28 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple @Override public Mono decodeToMono(Publisher input, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { - return DataBufferUtils.join(input, this.maxInMemorySize) - .flatMap(dataBuffer -> { - try { - ObjectReader objectReader = getObjectReader(elementType, mimeType, hints); - return customizeReaderFromStream(objectReader, mimeType, elementType, hints) - .flatMap(reader -> { - try { - return Mono.justOrEmpty(decode(dataBuffer, reader, hints)); - } - catch (DecodingException ex) { - return Mono.error(ex); - } - }); - } - catch (IllegalStateException ex) { - return Mono.error(ex); - } - }); + + return Mono.deferContextual(contextView -> { + + Map hintsToUse = contextView.isEmpty() ? hints : + Hints.merge(hints, ContextView.class.getName(), contextView); + + return DataBufferUtils.join(input, this.maxInMemorySize).flatMap(dataBuffer -> + Mono.justOrEmpty(decode(dataBuffer, elementType, mimeType, hintsToUse))); + }); } @Override public Object decode(DataBuffer dataBuffer, ResolvableType targetType, @Nullable MimeType mimeType, @Nullable Map hints) throws DecodingException { - ObjectReader reader = getObjectReader(targetType, mimeType, hints); - reader = customizeReader(reader, mimeType, targetType, hints); - return decode(dataBuffer, reader, hints); - } - private Object decode(@NonNull DataBuffer dataBuffer, @NonNull ObjectReader objectReader, - @Nullable Map hints) throws DecodingException { + ObjectMapper mapper = selectObjectMapper(targetType, mimeType); + if (mapper == null) { + throw new IllegalStateException("No ObjectMapper for " + targetType); + } + try { + ObjectReader objectReader = createObjectReader(mapper, targetType, hints); Object value = objectReader.readValue(dataBuffer.asInputStream()); logValue(value, hints); return value; @@ -220,16 +215,7 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple } } - private ObjectReader getObjectReader(ResolvableType targetType, @Nullable MimeType mimeType, - @Nullable Map hints) { - ObjectMapper mapper = selectObjectMapper(targetType, mimeType); - if (mapper == null) { - throw new IllegalStateException("No ObjectMapper for " + targetType); - } - return getObjectReader(mapper, targetType, hints); - } - - private ObjectReader getObjectReader( + private ObjectReader createObjectReader( ObjectMapper mapper, ResolvableType elementType, @Nullable Map hints) { Assert.notNull(elementType, "'elementType' must not be null"); @@ -239,34 +225,28 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple } JavaType javaType = getJavaType(elementType.getType(), contextClass); Class jsonView = (hints != null ? (Class) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null); - return jsonView != null ? + + ObjectReader objectReader = (jsonView != null ? mapper.readerWithView(jsonView).forType(javaType) : - mapper.readerFor(javaType); - } + mapper.readerFor(javaType)); - /** - * Provides the ability for subclasses to customize the {@link ObjectReader} for deserialization from a stream. - * @param reader the {@link ObjectReader} available for customization - * @param mimeType the MIME type associated with the input stream - * @param elementType the expected type of elements in the output stream - * @param hints additional information about how to do encode - * @return the customized {@link ObjectReader} - */ - protected Mono customizeReaderFromStream(@NonNull ObjectReader reader, @Nullable MimeType mimeType, - ResolvableType elementType, @Nullable Map hints) { - return Mono.just(customizeReader(reader, mimeType, elementType, hints)); + return customizeReader(objectReader, elementType, hints); } /** - * Provides the ability for subclasses to customize the {@link ObjectReader} for deserialization. - * @param reader the {@link ObjectReader} available for customization - * @param mimeType the MIME type associated with the input stream - * @param elementType the expected type of elements in the output stream - * @param hints additional information about how to do encode - * @return the customized {@link ObjectReader} + * Subclasses can use this method to customize {@link ObjectReader} used + * for reading values. + * @param reader the reader instance to customize + * @param elementType the target type of element values to read to + * @param hints a map with serialization hints; + * the Reactor Context, when available, may be accessed under the key + * {@code ContextView.class.getName()} + * @return the customized {@code ObjectReader} to use + * @since 6.0 */ - protected ObjectReader customizeReader(@NonNull ObjectReader reader, @Nullable MimeType mimeType, - ResolvableType elementType, @Nullable Map hints) { + protected ObjectReader customizeReader( + ObjectReader reader, ResolvableType elementType, @Nullable Map hints) { + return reader; } @@ -312,10 +292,6 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple return getMimeTypes(); } - @Override - public List getDecodableMimeTypes(ResolvableType targetType) { - return getMimeTypes(targetType); - } // Jackson2CodecSupport diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java index d55db2b6cf1..dd1ce11ee70 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java @@ -39,6 +39,7 @@ import com.fasterxml.jackson.databind.ser.FilterProvider; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.util.context.ContextView; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; @@ -53,7 +54,6 @@ import org.springframework.http.codec.HttpMessageEncoder; import org.springframework.http.converter.json.MappingJacksonValue; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -87,6 +87,7 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple private final List streamingMediaTypes = new ArrayList<>(1); + /** * Constructor with a Jackson {@link ObjectMapper} to use. */ @@ -148,89 +149,95 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple Assert.notNull(bufferFactory, "'bufferFactory' must not be null"); Assert.notNull(elementType, "'elementType' must not be null"); - if (inputStream instanceof Mono) { - return Mono.from(inputStream) - .flatMap(value -> createEncodingToolsForStream(value, elementType, mimeType, hints) - .map(tools -> encodeValue(value, tools.mapper(), tools.writer(), - bufferFactory, mimeType, hints))) - .flux(); - } + return Flux.deferContextual(contextView -> { - try { - ObjectMapper mapper = selectObjectMapper(elementType, mimeType); - if (mapper == null) { - throw new IllegalStateException("No ObjectMapper for " + elementType); - } - ObjectWriter writer = createObjectWriter(mapper, elementType, mimeType, null, hints); - ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.getFactory()._getBufferRecycler()); - JsonEncoding encoding = getJsonEncoding(mimeType); - JsonGenerator generator = mapper.getFactory().createGenerator(byteBuilder, encoding); - SequenceWriter sequenceWriter = writer.writeValues(generator); - - byte[] separator = getStreamingMediaTypeSeparator(mimeType); - Flux dataBufferFlux; + Map hintsToUse = contextView.isEmpty() ? hints : + Hints.merge(hints, ContextView.class.getName(), contextView); - if (separator != null) { - dataBufferFlux = Flux.from(inputStream).map(value -> encodeStreamingValue( - value, bufferFactory, hints, sequenceWriter, byteBuilder, EMPTY_BYTES, separator)); + if (inputStream instanceof Mono) { + return Mono.from(inputStream) + .map(value -> encodeValue(value, bufferFactory, elementType, mimeType, hintsToUse)) + .flux(); } - else { - JsonArrayJoinHelper helper = new JsonArrayJoinHelper(); - - // Do not prepend JSON array prefix until first signal is known, onNext vs onError - // Keeps response not committed for error handling - - dataBufferFlux = Flux.from(inputStream) - .map(value -> { - byte[] prefix = helper.getPrefix(); - byte[] delimiter = helper.getDelimiter(); - - DataBuffer dataBuffer = encodeStreamingValue( - value, bufferFactory, hints, sequenceWriter, byteBuilder, delimiter, EMPTY_BYTES); - - return (prefix.length > 0 ? - bufferFactory.join(Arrays.asList(bufferFactory.wrap(prefix), dataBuffer)) : - dataBuffer); - }) - .concatWith(Mono.fromCallable(() -> bufferFactory.wrap(helper.getSuffix()))); + try { + ObjectMapper mapper = selectObjectMapper(elementType, mimeType); + if (mapper == null) { + throw new IllegalStateException("No ObjectMapper for " + elementType); + } + + ObjectWriter writer = createObjectWriter(mapper, elementType, mimeType, null, hintsToUse); + ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.getFactory()._getBufferRecycler()); + JsonEncoding encoding = getJsonEncoding(mimeType); + JsonGenerator generator = mapper.getFactory().createGenerator(byteBuilder, encoding); + SequenceWriter sequenceWriter = writer.writeValues(generator); + + byte[] separator = getStreamingMediaTypeSeparator(mimeType); + Flux dataBufferFlux; + + if (separator != null) { + dataBufferFlux = Flux.from(inputStream).map(value -> encodeStreamingValue( + value, bufferFactory, hintsToUse, sequenceWriter, byteBuilder, EMPTY_BYTES, separator)); + } + else { + JsonArrayJoinHelper helper = new JsonArrayJoinHelper(); + + // Do not prepend JSON array prefix until first signal is known, onNext vs onError + // Keeps response not committed for error handling + + dataBufferFlux = Flux.from(inputStream) + .map(value -> { + byte[] prefix = helper.getPrefix(); + byte[] delimiter = helper.getDelimiter(); + + DataBuffer dataBuffer = encodeStreamingValue( + value, bufferFactory, hintsToUse, sequenceWriter, byteBuilder, + delimiter, EMPTY_BYTES); + + return (prefix.length > 0 ? + bufferFactory.join(Arrays.asList(bufferFactory.wrap(prefix), dataBuffer)) : + dataBuffer); + }) + .concatWith(Mono.fromCallable(() -> bufferFactory.wrap(helper.getSuffix()))); + } + + return dataBufferFlux + .doOnNext(dataBuffer -> Hints.touchDataBuffer(dataBuffer, hintsToUse, logger)) + .doAfterTerminate(() -> { + try { + byteBuilder.release(); + generator.close(); + } + catch (IOException ex) { + logger.error("Could not close Encoder resources", ex); + } + }); } - - return dataBufferFlux - .doOnNext(dataBuffer -> Hints.touchDataBuffer(dataBuffer, hints, logger)) - .doAfterTerminate(() -> { - try { - byteBuilder.release(); - generator.close(); - } - catch (IOException ex) { - logger.error("Could not close Encoder resources", ex); - } - }); - } - catch (IOException ex) { - return Flux.error(ex); - } + catch (IOException ex) { + return Flux.error(ex); + } + }); } @Override public DataBuffer encodeValue(Object value, DataBufferFactory bufferFactory, ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map hints) { - ObjectEncodingTools encodingTools = createEncodingTools(value, valueType, mimeType, hints); - ObjectWriter writer = encodingTools.writer(); - writer = customizeWriter(writer, mimeType, valueType, hints); - return encodeValue(value, encodingTools.mapper(), writer, bufferFactory, mimeType, hints); - } - - private DataBuffer encodeValue(Object value, ObjectMapper mapper, ObjectWriter writer, - DataBufferFactory bufferFactory, @Nullable MimeType mimeType, @Nullable Map hints) { + Class jsonView = null; FilterProvider filters = null; if (value instanceof MappingJacksonValue mappingJacksonValue) { value = mappingJacksonValue.getValue(); + valueType = ResolvableType.forInstance(value); + jsonView = mappingJacksonValue.getSerializationView(); filters = mappingJacksonValue.getFilters(); } + ObjectMapper mapper = selectObjectMapper(valueType, mimeType); + if (mapper == null) { + throw new IllegalStateException("No ObjectMapper for " + valueType); + } + + ObjectWriter writer = createObjectWriter(mapper, valueType, mimeType, jsonView, hints); if (filters != null) { writer = writer.with(filters); } @@ -324,35 +331,6 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple } } - private Mono createEncodingToolsForStream(Object value, ResolvableType valueType, - @Nullable MimeType mimeType, @Nullable Map hints) { - try { - ObjectEncodingTools encodingTools = createEncodingTools(value, valueType, mimeType, hints); - ObjectWriter objectWriter = encodingTools.writer(); - return customizeWriterFromStream(objectWriter, mimeType, valueType, hints) - .map(customizedWriter -> new ObjectEncodingTools(encodingTools.mapper(), customizedWriter)); - } - catch (IllegalStateException ex) { - return Mono.error(ex); - } - } - - private ObjectEncodingTools createEncodingTools(Object value, ResolvableType valueType, - @Nullable MimeType mimeType, @Nullable Map hints) { - Class jsonView = null; - if (value instanceof MappingJacksonValue mappingJacksonValue) { - valueType = ResolvableType.forInstance(mappingJacksonValue.getValue()); - jsonView = mappingJacksonValue.getSerializationView(); - } - - ObjectMapper mapper = selectObjectMapper(valueType, mimeType); - if (mapper == null) { - throw new IllegalStateException("No ObjectMapper for " + valueType); - } - ObjectWriter writer = createObjectWriter(mapper, valueType, mimeType, jsonView, hints); - return new ObjectEncodingTools(mapper, writer); - } - private ObjectWriter createObjectWriter( ObjectMapper mapper, ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Class jsonView, @Nullable Map hints) { @@ -365,32 +343,23 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple if (javaType.isContainerType()) { writer = writer.forType(javaType); } - return writer; + return customizeWriter(writer, mimeType, valueType, hints); } /** - * Provides the ability for subclasses to customize the {@link ObjectWriter} for serialization from a stream. - * @param writer the {@link ObjectWriter} available for customization - * @param mimeType the MIME type associated with the input stream - * @param elementType the expected type of elements in the output stream - * @param hints additional information about how to do encode - * @return the customized {@link ObjectWriter} - */ - protected Mono customizeWriterFromStream(@NonNull ObjectWriter writer, @Nullable MimeType mimeType, - ResolvableType elementType, @Nullable Map hints) { - return Mono.just(customizeWriter(writer, mimeType, elementType, hints)); - } - - /** - * Provides the ability for subclasses to customize the {@link ObjectWriter} for serialization. - * @param writer the {@link ObjectWriter} available for customization - * @param mimeType the MIME type associated with the input stream - * @param elementType the expected type of elements in the output stream - * @param hints additional information about how to do encode - * @return the customized {@link ObjectWriter} + * Subclasses can use this method to customize {@link ObjectWriter} used + * for writing values. + * @param writer the writer instance to customize + * @param mimeType the selected MIME type + * @param elementType the type of element values to write + * @param hints a map with serialization hints; + * the Reactor Context, when available, may be accessed under the key + * {@code ContextView.class.getName()} + * @return the customized {@code ObjectWriter} to use */ protected ObjectWriter customizeWriter(ObjectWriter writer, @Nullable MimeType mimeType, ResolvableType elementType, @Nullable Map hints) { + return writer; } @@ -489,8 +458,4 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple } } - - private record ObjectEncodingTools(ObjectMapper mapper, ObjectWriter writer) { - } - } diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java index a35ae7c87fd..e47f96debae 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java @@ -59,7 +59,6 @@ import org.springframework.http.converter.HttpMessageConversionException; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -411,12 +410,14 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener } /** - * Provides the ability for subclasses to customize the {@link ObjectReader} for deserialization. - * @param reader the {@link ObjectReader} available for customization - * @param javaType the specified type to deserialize to + * Subclasses can use this method to customize {@link ObjectReader} used + * for reading values. + * @param reader the reader instance to customize + * @param javaType the target type of element values to read to * @return the customized {@link ObjectReader} + * @since 6.0 */ - protected ObjectReader customizeReader(@NonNull ObjectReader reader, JavaType javaType) { + protected ObjectReader customizeReader(ObjectReader reader, JavaType javaType) { return reader; } @@ -495,14 +496,17 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener } /** - * Provides the ability for subclasses to customize the {@link ObjectWriter} for serialization. - * @param writer the {@link ObjectWriter} available for customization - * @param javaType the specified type to serialize from - * @param contentType the output content type + * Subclasses can use this method to customize {@link ObjectWriter} used + * for writing values. + * @param writer the writer instance to customize + * @param javaType the type of element values to write + * @param contentType the selected media type * @return the customized {@link ObjectWriter} + * @since 6.0 */ - protected ObjectWriter customizeWriter(@NonNull ObjectWriter writer, @Nullable JavaType javaType, - @Nullable MediaType contentType) { + protected ObjectWriter customizeWriter( + ObjectWriter writer, @Nullable JavaType javaType, @Nullable MediaType contentType) { + return writer; } diff --git a/spring-web/src/test/java/org/springframework/http/codec/json/CustomizedJackson2JsonDecoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/json/CustomizedJackson2JsonDecoderTests.java index bfd456953be..98de2af708c 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/json/CustomizedJackson2JsonDecoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/json/CustomizedJackson2JsonDecoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,6 @@ import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.testfixture.codec.AbstractDecoderTests; -import org.springframework.util.MimeType; /** * Unit tests for a customized {@link Jackson2JsonDecoder}. @@ -83,7 +82,8 @@ public class CustomizedJackson2JsonDecoderTests extends AbstractDecoderTests customizeReaderFromStream(ObjectReader reader, MimeType mimeType, ResolvableType elementType, Map hints) { - return Mono.just(reader.with(DeserializationFeature.READ_ENUMS_USING_TO_STRING)); - } + protected ObjectReader customizeReader( + ObjectReader reader, ResolvableType elementType, Map hints) { - @Override - protected ObjectReader customizeReader(ObjectReader reader, MimeType mimeType, ResolvableType elementType, Map hints) { return reader.with(DeserializationFeature.READ_ENUMS_USING_TO_STRING); } } + } diff --git a/spring-web/src/test/java/org/springframework/http/codec/json/CustomizedJackson2JsonEncoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/json/CustomizedJackson2JsonEncoderTests.java index dcfb6f26630..0441dd13ad3 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/json/CustomizedJackson2JsonEncoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/json/CustomizedJackson2JsonEncoderTests.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationFeature; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; import org.springframework.core.io.buffer.DataBufferUtils; @@ -71,14 +70,14 @@ public class CustomizedJackson2JsonEncoderTests extends AbstractEncoderTests step - .consumeNextWith(expectString("[" + - "{\"property\":\"Value1\"}," + - "{\"property\":\"Value2\"}]") - .andThen(DataBufferUtils::release)) + .consumeNextWith(expectString("[{\"property\":\"Value1\"}").andThen(DataBufferUtils::release)) + .consumeNextWith(expectString(",{\"property\":\"Value2\"}").andThen(DataBufferUtils::release)) + .consumeNextWith(expectString("]").andThen(DataBufferUtils::release)) .verifyComplete()); } - public static class MyCustomizedEncoderBean { + + private static class MyCustomizedEncoderBean { private MyCustomEncoderEnum property; @@ -95,7 +94,8 @@ public class CustomizedJackson2JsonEncoderTests extends AbstractEncoderTests customizeWriterFromStream(ObjectWriter writer, MimeType mimeType, ResolvableType elementType, Map hints) { - return Mono.just(writer.with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)); - } + protected ObjectWriter customizeWriter( + ObjectWriter writer, MimeType mimeType, ResolvableType elementType, Map hints) { - @Override - protected ObjectWriter customizeWriter(ObjectWriter writer, MimeType mimeType, ResolvableType elementType, Map hints) { return writer.with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); } } + }