Introduce Jackson 3 support for codecs
This commit introduces Jackson 3 variants of the following Jackson 2 classes (and related dependent classes). org.springframework.http.codec.json.Jackson2CodecSupport -> org.springframework.http.codec.JacksonCodecSupport org.springframework.http.codec.json.Jackson2Tokenizer -> org.springframework.http.codec.JacksonTokenizer org.springframework.http.codec.json.Jackson2SmileDecoder -> org.springframework.http.codec.smile.JacksonSmileDecoder org.springframework.http.codec.json.Jackson2SmileEncoder -> org.springframework.http.codec.smile.JacksonSmileEncoder Jackson2CborDecoder -> JacksonCborDecoder Jackson2CborEncoder -> JacksonCborEncoder Jackson2JsonDecoder -> JacksonJsonDecoder Jackson2JsonEncoder -> JacksonJsonEncoder Jackson 3 support is configured if found in the classpath otherwise fallback to Jackson 2. See gh-33798
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.CompositeByteBuf;
|
||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.core.JsonToken;
|
||||
import tools.jackson.core.TreeNode;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.databind.util.TokenBuffer;
|
||||
|
||||
import org.springframework.core.codec.DecodingException;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferLimitException;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.core.testfixture.io.buffer.AbstractLeakCheckingTests;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JacksonTokenizer}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class JacksonTokenizerTests extends AbstractLeakCheckingTests {
|
||||
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void createParser() {
|
||||
this.objectMapper = JsonMapper.builder().build();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void doNotTokenizeArrayElements() {
|
||||
testTokenize(
|
||||
singletonList("{\"foo\": \"foofoo\", \"bar\": \"barbar\"}"),
|
||||
singletonList("{\"foo\": \"foofoo\", \"bar\": \"barbar\"}"), false);
|
||||
|
||||
testTokenize(
|
||||
asList(
|
||||
"{\"foo\": \"foofoo\"",
|
||||
", \"bar\": \"barbar\"}"
|
||||
),
|
||||
singletonList("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}"), false);
|
||||
|
||||
testTokenize(
|
||||
singletonList("[{\"foo\": \"foofoo\", \"bar\": \"barbar\"},{\"foo\": \"foofoofoo\", \"bar\": \"barbarbar\"}]"),
|
||||
singletonList("[{\"foo\": \"foofoo\", \"bar\": \"barbar\"},{\"foo\": \"foofoofoo\", \"bar\": \"barbarbar\"}]"),
|
||||
false);
|
||||
|
||||
testTokenize(
|
||||
singletonList("[{\"foo\": \"bar\"},{\"foo\": \"baz\"}]"),
|
||||
singletonList("[{\"foo\": \"bar\"},{\"foo\": \"baz\"}]"), false);
|
||||
|
||||
testTokenize(
|
||||
asList(
|
||||
"[{\"foo\": \"foofoo\", \"bar\"",
|
||||
": \"barbar\"},{\"foo\": \"foofoofoo\", \"bar\": \"barbarbar\"}]"
|
||||
),
|
||||
singletonList("[{\"foo\": \"foofoo\", \"bar\": \"barbar\"},{\"foo\": \"foofoofoo\", \"bar\": \"barbarbar\"}]"),
|
||||
false);
|
||||
|
||||
testTokenize(
|
||||
asList(
|
||||
"[",
|
||||
"{\"id\":1,\"name\":\"Robert\"}", ",",
|
||||
"{\"id\":2,\"name\":\"Raide\"}", ",",
|
||||
"{\"id\":3,\"name\":\"Ford\"}", "]"
|
||||
),
|
||||
singletonList("[{\"id\":1,\"name\":\"Robert\"},{\"id\":2,\"name\":\"Raide\"},{\"id\":3,\"name\":\"Ford\"}]"),
|
||||
false);
|
||||
|
||||
// SPR-16166: top-level JSON values
|
||||
testTokenize(asList("\"foo", "bar\""), singletonList("\"foobar\""), false);
|
||||
testTokenize(asList("12", "34"), singletonList("1234"), false);
|
||||
testTokenize(asList("12.", "34"), singletonList("12.34"), false);
|
||||
|
||||
// note that we do not test for null, true, or false, which are also valid top-level values,
|
||||
// but are unsupported by JSONassert
|
||||
}
|
||||
|
||||
@Test
|
||||
void tokenizeArrayElements() {
|
||||
testTokenize(
|
||||
singletonList("{\"foo\": \"foofoo\", \"bar\": \"barbar\"}"),
|
||||
singletonList("{\"foo\": \"foofoo\", \"bar\": \"barbar\"}"), true);
|
||||
|
||||
testTokenize(
|
||||
asList(
|
||||
"{\"foo\": \"foofoo\"",
|
||||
", \"bar\": \"barbar\"}"
|
||||
),
|
||||
singletonList("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}"), true);
|
||||
|
||||
testTokenize(
|
||||
singletonList("[{\"foo\": \"foofoo\", \"bar\": \"barbar\"},{\"foo\": \"foofoofoo\", \"bar\": \"barbarbar\"}]"),
|
||||
asList(
|
||||
"{\"foo\": \"foofoo\", \"bar\": \"barbar\"}",
|
||||
"{\"foo\": \"foofoofoo\", \"bar\": \"barbarbar\"}"
|
||||
),
|
||||
true);
|
||||
|
||||
testTokenize(
|
||||
singletonList("[{\"foo\": \"bar\"},{\"foo\": \"baz\"}]"),
|
||||
asList(
|
||||
"{\"foo\": \"bar\"}",
|
||||
"{\"foo\": \"baz\"}"
|
||||
),
|
||||
true);
|
||||
|
||||
// SPR-15803: nested array
|
||||
testTokenize(
|
||||
singletonList("[" +
|
||||
"{\"id\":\"0\",\"start\":[-999999999,1,1],\"end\":[999999999,12,31]}," +
|
||||
"{\"id\":\"1\",\"start\":[-999999999,1,1],\"end\":[999999999,12,31]}," +
|
||||
"{\"id\":\"2\",\"start\":[-999999999,1,1],\"end\":[999999999,12,31]}" +
|
||||
"]"),
|
||||
asList(
|
||||
"{\"id\":\"0\",\"start\":[-999999999,1,1],\"end\":[999999999,12,31]}",
|
||||
"{\"id\":\"1\",\"start\":[-999999999,1,1],\"end\":[999999999,12,31]}",
|
||||
"{\"id\":\"2\",\"start\":[-999999999,1,1],\"end\":[999999999,12,31]}"
|
||||
),
|
||||
true);
|
||||
|
||||
// SPR-15803: nested array, no top-level array
|
||||
testTokenize(
|
||||
singletonList("{\"speakerIds\":[\"tastapod\"],\"language\":\"ENGLISH\"}"),
|
||||
singletonList("{\"speakerIds\":[\"tastapod\"],\"language\":\"ENGLISH\"}"), true);
|
||||
|
||||
testTokenize(
|
||||
asList(
|
||||
"[{\"foo\": \"foofoo\", \"bar\"",
|
||||
": \"barbar\"},{\"foo\": \"foofoofoo\", \"bar\": \"barbarbar\"}]"
|
||||
),
|
||||
asList(
|
||||
"{\"foo\": \"foofoo\", \"bar\": \"barbar\"}",
|
||||
"{\"foo\": \"foofoofoo\", \"bar\": \"barbarbar\"}"), true);
|
||||
|
||||
testTokenize(
|
||||
asList(
|
||||
"[",
|
||||
"{\"id\":1,\"name\":\"Robert\"}",
|
||||
",",
|
||||
"{\"id\":2,\"name\":\"Raide\"}",
|
||||
",",
|
||||
"{\"id\":3,\"name\":\"Ford\"}",
|
||||
"]"
|
||||
),
|
||||
asList(
|
||||
"{\"id\":1,\"name\":\"Robert\"}",
|
||||
"{\"id\":2,\"name\":\"Raide\"}",
|
||||
"{\"id\":3,\"name\":\"Ford\"}"
|
||||
),
|
||||
true);
|
||||
|
||||
// SPR-16166: top-level JSON values
|
||||
testTokenize(asList("\"foo", "bar\""), singletonList("\"foobar\""), true);
|
||||
testTokenize(asList("12", "34"), singletonList("1234"), true);
|
||||
testTokenize(asList("12.", "34"), singletonList("12.34"), true);
|
||||
|
||||
// SPR-16407
|
||||
testTokenize(asList("[1", ",2,", "3]"), asList("1", "2", "3"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void tokenizeStream() {
|
||||
|
||||
// NDJSON (Newline Delimited JSON), JSON Lines
|
||||
testTokenize(
|
||||
asList(
|
||||
"{\"id\":1,\"name\":\"Robert\"}",
|
||||
"\n",
|
||||
"{\"id\":2,\"name\":\"Raide\"}",
|
||||
"\n",
|
||||
"{\"id\":3,\"name\":\"Ford\"}"
|
||||
),
|
||||
asList(
|
||||
"{\"id\":1,\"name\":\"Robert\"}",
|
||||
"{\"id\":2,\"name\":\"Raide\"}",
|
||||
"{\"id\":3,\"name\":\"Ford\"}"
|
||||
),
|
||||
true);
|
||||
|
||||
// JSON Sequence with newline separator
|
||||
testTokenize(
|
||||
asList(
|
||||
"\n",
|
||||
"{\"id\":1,\"name\":\"Robert\"}",
|
||||
"\n",
|
||||
"{\"id\":2,\"name\":\"Raide\"}",
|
||||
"\n",
|
||||
"{\"id\":3,\"name\":\"Ford\"}"
|
||||
),
|
||||
asList(
|
||||
"{\"id\":1,\"name\":\"Robert\"}",
|
||||
"{\"id\":2,\"name\":\"Raide\"}",
|
||||
"{\"id\":3,\"name\":\"Ford\"}"
|
||||
),
|
||||
true);
|
||||
}
|
||||
|
||||
private void testTokenize(List<String> input, List<String> output, boolean tokenize) {
|
||||
StepVerifier.FirstStep<String> builder = StepVerifier.create(decode(input, tokenize, -1));
|
||||
output.forEach(expected -> builder.assertNext(actual -> {
|
||||
try {
|
||||
JSONAssert.assertEquals(expected, actual, true);
|
||||
}
|
||||
catch (JSONException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}));
|
||||
builder.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLimit() {
|
||||
List<String> source = asList(
|
||||
"[",
|
||||
"{", "\"id\":1,\"name\":\"Dan\"", "},",
|
||||
"{", "\"id\":2,\"name\":\"Ron\"", "},",
|
||||
"{", "\"id\":3,\"name\":\"Bartholomew\"", "}",
|
||||
"]"
|
||||
);
|
||||
|
||||
String expected = String.join("", source);
|
||||
int maxInMemorySize = expected.length();
|
||||
|
||||
StepVerifier.create(decode(source, false, maxInMemorySize))
|
||||
.expectNext(expected)
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(decode(source, false, maxInMemorySize - 2))
|
||||
.verifyError(DataBufferLimitException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLimitTokenized() {
|
||||
|
||||
List<String> source = asList(
|
||||
"[",
|
||||
"{", "\"id\":1, \"name\":\"Dan\"", "},",
|
||||
"{", "\"id\":2, \"name\":\"Ron\"", "},",
|
||||
"{", "\"id\":3, \"name\":\"Bartholomew\"", "}",
|
||||
"]"
|
||||
);
|
||||
|
||||
String expected = "{\"id\":3,\"name\":\"Bartholomew\"}";
|
||||
int maxInMemorySize = expected.length();
|
||||
|
||||
StepVerifier.create(decode(source, true, maxInMemorySize))
|
||||
.expectNext("{\"id\":1,\"name\":\"Dan\"}")
|
||||
.expectNext("{\"id\":2,\"name\":\"Ron\"}")
|
||||
.expectNext(expected)
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(decode(source, true, maxInMemorySize - 1))
|
||||
.expectNext("{\"id\":1,\"name\":\"Dan\"}")
|
||||
.expectNext("{\"id\":2,\"name\":\"Ron\"}")
|
||||
.verifyError(DataBufferLimitException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorInStream() {
|
||||
DataBuffer buffer = stringBuffer("{\"id\":1,\"name\":");
|
||||
Flux<DataBuffer> source = Flux.just(buffer).concatWith(Flux.error(new RuntimeException()));
|
||||
Flux<TokenBuffer> result = JacksonTokenizer.tokenize(source, this.objectMapper, true, false, -1);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError(RuntimeException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test // SPR-16521
|
||||
public void jsonEOFExceptionIsWrappedAsDecodingError() {
|
||||
Flux<DataBuffer> source = Flux.just(stringBuffer("{\"status\": \"noClosingQuote}"));
|
||||
Flux<TokenBuffer> tokens = JacksonTokenizer.tokenize(source, this.objectMapper, false, false, -1);
|
||||
|
||||
StepVerifier.create(tokens)
|
||||
.expectError(DecodingException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void useBigDecimalForFloats() {
|
||||
Flux<DataBuffer> source = Flux.just(stringBuffer("1E+2"));
|
||||
Flux<TokenBuffer> tokens = JacksonTokenizer.tokenize(
|
||||
source, this.objectMapper, false, true, -1);
|
||||
|
||||
StepVerifier.create(tokens)
|
||||
.assertNext(tokenBuffer -> {
|
||||
JsonParser parser = tokenBuffer.asParser();
|
||||
JsonToken token = parser.nextToken();
|
||||
assertThat(token).isEqualTo(JsonToken.VALUE_NUMBER_FLOAT);
|
||||
JsonParser.NumberType numberType = parser.getNumberType();
|
||||
assertThat(numberType).isEqualTo(JsonParser.NumberType.BIG_DECIMAL);
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// gh-31747
|
||||
@Test
|
||||
void compositeNettyBuffer() {
|
||||
ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
|
||||
ByteBuf firstByteBuf = allocator.buffer();
|
||||
firstByteBuf.writeBytes("{\"foo\": \"foofoo\"".getBytes(StandardCharsets.UTF_8));
|
||||
ByteBuf secondBuf = allocator.buffer();
|
||||
secondBuf.writeBytes(", \"bar\": \"barbar\"}".getBytes(StandardCharsets.UTF_8));
|
||||
CompositeByteBuf composite = allocator.compositeBuffer();
|
||||
composite.addComponent(true, firstByteBuf);
|
||||
composite.addComponent(true, secondBuf);
|
||||
|
||||
NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(allocator);
|
||||
Flux<DataBuffer> source = Flux.just(bufferFactory.wrap(composite));
|
||||
Flux<TokenBuffer> tokens = JacksonTokenizer.tokenize(source, this.objectMapper, false, false, -1);
|
||||
|
||||
Flux<String> strings = tokens.map(this::tokenToString);
|
||||
|
||||
StepVerifier.create(strings)
|
||||
.assertNext(s -> assertThat(s).isEqualTo("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
|
||||
private Flux<String> decode(List<String> source, boolean tokenize, int maxInMemorySize) {
|
||||
|
||||
Flux<TokenBuffer> tokens = JacksonTokenizer.tokenize(
|
||||
Flux.fromIterable(source).map(this::stringBuffer), this.objectMapper, tokenize, false, maxInMemorySize);
|
||||
|
||||
return tokens.map(this::tokenToString);
|
||||
}
|
||||
|
||||
private String tokenToString(TokenBuffer tokenBuffer) {
|
||||
TreeNode root = this.objectMapper.readTree(tokenBuffer.asParser());
|
||||
return this.objectMapper.writeValueAsString(root);
|
||||
}
|
||||
|
||||
private DataBuffer stringBuffer(String value) {
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
DataBuffer buffer = this.bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import org.springframework.core.io.buffer.DataBufferLimitException;
|
||||
import org.springframework.core.testfixture.io.buffer.AbstractLeakCheckingTests;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.web.testfixture.xml.Pojo;
|
||||
|
||||
@@ -44,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
class ServerSentEventHttpMessageReaderTests extends AbstractLeakCheckingTests {
|
||||
|
||||
private final Jackson2JsonDecoder jsonDecoder = new Jackson2JsonDecoder();
|
||||
private final JacksonJsonDecoder jsonDecoder = new JacksonJsonDecoder();
|
||||
|
||||
private ServerSentEventHttpMessageReader reader = new ServerSentEventHttpMessageReader(this.jsonDecoder);
|
||||
|
||||
|
||||
@@ -22,19 +22,19 @@ import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import tools.jackson.databind.SerializationFeature;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.testfixture.io.buffer.AbstractDataBufferAllocatingTests;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.web.testfixture.xml.Pojo;
|
||||
|
||||
@@ -54,7 +54,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
|
||||
private static final Map<String, Object> HINTS = Collections.emptyMap();
|
||||
|
||||
private ServerSentEventHttpMessageWriter messageWriter =
|
||||
new ServerSentEventHttpMessageWriter(new Jackson2JsonEncoder());
|
||||
new ServerSentEventHttpMessageWriter(new JacksonJsonEncoder());
|
||||
|
||||
|
||||
@ParameterizedDataBufferAllocatingTest
|
||||
@@ -151,10 +151,10 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
|
||||
|
||||
StepVerifier.create(outputMessage.getBody())
|
||||
.consumeNextWith(stringConsumer("data:"))
|
||||
.consumeNextWith(stringConsumer("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}"))
|
||||
.consumeNextWith(stringConsumer("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}"))
|
||||
.consumeNextWith(stringConsumer("\n\n"))
|
||||
.consumeNextWith(stringConsumer("data:"))
|
||||
.consumeNextWith(stringConsumer("{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}"))
|
||||
.consumeNextWith(stringConsumer("{\"bar\":\"barbarbar\",\"foo\":\"foofoofoo\"}"))
|
||||
.consumeNextWith(stringConsumer("\n\n"))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
@@ -164,8 +164,8 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
|
||||
void writePojoWithPrettyPrint(DataBufferFactory bufferFactory) {
|
||||
super.bufferFactory = bufferFactory;
|
||||
|
||||
ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().indentOutput(true).build();
|
||||
this.messageWriter = new ServerSentEventHttpMessageWriter(new Jackson2JsonEncoder(mapper));
|
||||
JsonMapper mapper = JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build();
|
||||
this.messageWriter = new ServerSentEventHttpMessageWriter(new JacksonJsonEncoder(mapper));
|
||||
|
||||
MockServerHttpResponse outputMessage = new MockServerHttpResponse(super.bufferFactory);
|
||||
Flux<Pojo> source = Flux.just(new Pojo("foofoo", "barbar"), new Pojo("foofoofoo", "barbarbar"));
|
||||
@@ -175,15 +175,15 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
|
||||
.consumeNextWith(stringConsumer("data:"))
|
||||
.consumeNextWith(stringConsumer("""
|
||||
{
|
||||
data: "foo" : "foofoo",
|
||||
data: "bar" : "barbar"
|
||||
data: "bar" : "barbar",
|
||||
data: "foo" : "foofoo"
|
||||
data:}"""))
|
||||
.consumeNextWith(stringConsumer("\n\n"))
|
||||
.consumeNextWith(stringConsumer("data:"))
|
||||
.consumeNextWith(stringConsumer("""
|
||||
{
|
||||
data: "foo" : "foofoofoo",
|
||||
data: "bar" : "barbarbar"
|
||||
data: "bar" : "barbarbar",
|
||||
data: "foo" : "foofoofoo"
|
||||
data:}"""))
|
||||
.consumeNextWith(stringConsumer("\n\n"))
|
||||
.expectComplete()
|
||||
@@ -203,7 +203,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
|
||||
assertThat(outputMessage.getHeaders().getContentType()).isEqualTo(mediaType);
|
||||
StepVerifier.create(outputMessage.getBody())
|
||||
.consumeNextWith(stringConsumer("data:", charset))
|
||||
.consumeNextWith(stringConsumer("{\"foo\":\"foo\uD834\uDD1E\",\"bar\":\"bar\uD834\uDD1E\"}", charset))
|
||||
.consumeNextWith(stringConsumer("{\"bar\":\"bar\uD834\uDD1E\",\"foo\":\"foo\uD834\uDD1E\"}", charset))
|
||||
.consumeNextWith(stringConsumer("\n\n", charset))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec.cbor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.dataformat.cbor.CBORMapper;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.testfixture.codec.AbstractDecoderTests;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.testfixture.xml.Pojo;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
|
||||
/**
|
||||
* Tests for {@link JacksonCborDecoder}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class JacksonCborDecoderTests extends AbstractDecoderTests<JacksonCborDecoder> {
|
||||
|
||||
private Pojo pojo1 = new Pojo("f1", "b1");
|
||||
|
||||
private Pojo pojo2 = new Pojo("f2", "b2");
|
||||
|
||||
private CBORMapper mapper = CBORMapper.builder().build();
|
||||
|
||||
public JacksonCborDecoderTests() {
|
||||
super(new JacksonCborDecoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void canDecode() {
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), MediaType.APPLICATION_CBOR)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), null)).isTrue();
|
||||
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(String.class), null)).isFalse();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), APPLICATION_JSON)).isFalse();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void decode() {
|
||||
Flux<DataBuffer> input = Flux.just(this.pojo1, this.pojo2)
|
||||
.map(this::writeObject)
|
||||
.flatMap(this::dataBuffer);
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
testDecodeAll(input, Pojo.class, step -> step
|
||||
.expectNext(pojo1)
|
||||
.expectNext(pojo2)
|
||||
.verifyComplete()));
|
||||
|
||||
}
|
||||
|
||||
private byte[] writeObject(Object o) {
|
||||
try {
|
||||
return this.mapper.writer().writeValueAsBytes(o);
|
||||
}
|
||||
catch (JacksonException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void decodeToMono() {
|
||||
List<Pojo> expected = Arrays.asList(pojo1, pojo2);
|
||||
|
||||
Flux<DataBuffer> input = Flux.just(expected)
|
||||
.map(this::writeObject)
|
||||
.flatMap(this::dataBuffer);
|
||||
|
||||
ResolvableType elementType = ResolvableType.forClassWithGenerics(List.class, Pojo.class);
|
||||
testDecodeToMono(input, elementType, step -> step
|
||||
.expectNext(expected)
|
||||
.expectComplete()
|
||||
.verify(), null, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec.cbor;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import tools.jackson.dataformat.cbor.CBORMapper;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.testfixture.io.buffer.AbstractLeakCheckingTests;
|
||||
import org.springframework.core.testfixture.io.buffer.DataBufferTestUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.testfixture.xml.Pojo;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.springframework.core.io.buffer.DataBufferUtils.release;
|
||||
import static org.springframework.http.MediaType.APPLICATION_XML;
|
||||
|
||||
/**
|
||||
* Tests for {@link JacksonCborEncoder}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class JacksonCborEncoderTests extends AbstractLeakCheckingTests {
|
||||
|
||||
private final CBORMapper mapper = CBORMapper.builder().build();
|
||||
|
||||
private final JacksonCborEncoder encoder = new JacksonCborEncoder();
|
||||
|
||||
private Consumer<DataBuffer> pojoConsumer(Pojo expected) {
|
||||
return dataBuffer -> {
|
||||
Pojo actual = this.mapper.reader().forType(Pojo.class)
|
||||
.readValue(DataBufferTestUtils.dumpBytes(dataBuffer));
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
release(dataBuffer);
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
void canEncode() {
|
||||
ResolvableType pojoType = ResolvableType.forClass(Pojo.class);
|
||||
assertThat(this.encoder.canEncode(pojoType, MediaType.APPLICATION_CBOR)).isTrue();
|
||||
assertThat(this.encoder.canEncode(pojoType, null)).isTrue();
|
||||
|
||||
// SPR-15464
|
||||
assertThat(this.encoder.canEncode(ResolvableType.NONE, null)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void canNotEncode() {
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(String.class), null)).isFalse();
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), APPLICATION_XML)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void encode() {
|
||||
Pojo value = new Pojo("foo", "bar");
|
||||
DataBuffer result = encoder.encodeValue(value, this.bufferFactory, ResolvableType.forClass(Pojo.class),
|
||||
MediaType.APPLICATION_CBOR, null);
|
||||
pojoConsumer(value).accept(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeStream() {
|
||||
Pojo pojo1 = new Pojo("foo", "bar");
|
||||
Pojo pojo2 = new Pojo("foofoo", "barbar");
|
||||
Pojo pojo3 = new Pojo("foofoofoo", "barbarbar");
|
||||
Flux<Pojo> input = Flux.just(pojo1, pojo2, pojo3);
|
||||
ResolvableType type = ResolvableType.forClass(Pojo.class);
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
encoder.encode(input, this.bufferFactory, type, MediaType.APPLICATION_CBOR, null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec.json;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import tools.jackson.databind.ObjectReader;
|
||||
import tools.jackson.databind.cfg.EnumFeature;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.testfixture.codec.AbstractDecoderTests;
|
||||
|
||||
/**
|
||||
* Tests for a customized {@link JacksonJsonDecoder}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class CustomizedJacksonJsonDecoderTests extends AbstractDecoderTests<JacksonJsonDecoder> {
|
||||
|
||||
CustomizedJacksonJsonDecoderTests() {
|
||||
super(new JacksonJsonDecoderWithCustomization());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void canDecode() throws Exception {
|
||||
// Not Testing, covered under JacksonJsonDecoderTests
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void decode() throws Exception {
|
||||
Flux<DataBuffer> input = Flux.concat(stringBuffer("{\"property\":\"Value1\"}"));
|
||||
|
||||
testDecodeAll(input, MyCustomizedDecoderBean.class, step -> step
|
||||
.expectNextMatches(obj -> obj.getProperty() == MyCustomDecoderEnum.VAL1)
|
||||
.verifyComplete());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void decodeToMono() throws Exception {
|
||||
Mono<DataBuffer> input = stringBuffer("{\"property\":\"Value2\"}");
|
||||
|
||||
ResolvableType elementType = ResolvableType.forClass(MyCustomizedDecoderBean.class);
|
||||
|
||||
testDecodeToMono(input, elementType, step -> step
|
||||
.expectNextMatches(obj -> ((MyCustomizedDecoderBean)obj).getProperty() == MyCustomDecoderEnum.VAL2)
|
||||
.expectComplete()
|
||||
.verify(), null, null);
|
||||
}
|
||||
|
||||
private Mono<DataBuffer> stringBuffer(String value) {
|
||||
return stringBuffer(value, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private Mono<DataBuffer> stringBuffer(String value, Charset charset) {
|
||||
return Mono.defer(() -> {
|
||||
byte[] bytes = value.getBytes(charset);
|
||||
DataBuffer buffer = this.bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
return Mono.just(buffer);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static class MyCustomizedDecoderBean {
|
||||
|
||||
private MyCustomDecoderEnum property;
|
||||
|
||||
public MyCustomDecoderEnum getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setProperty(MyCustomDecoderEnum property) {
|
||||
this.property = property;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum MyCustomDecoderEnum {
|
||||
VAL1,
|
||||
VAL2;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this == VAL1 ? "Value1" : "Value2";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class JacksonJsonDecoderWithCustomization extends JacksonJsonDecoder {
|
||||
|
||||
@Override
|
||||
protected ObjectReader customizeReader(
|
||||
ObjectReader reader, ResolvableType elementType, Map<String, Object> hints) {
|
||||
|
||||
return reader.with(EnumFeature.READ_ENUMS_USING_TO_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import tools.jackson.databind.ObjectWriter;
|
||||
import tools.jackson.databind.cfg.EnumFeature;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.testfixture.codec.AbstractEncoderTests;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_NDJSON;
|
||||
|
||||
/**
|
||||
* Tests for a customized {@link JacksonJsonEncoder}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class CustomizedJacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> {
|
||||
|
||||
CustomizedJacksonJsonEncoderTests() {
|
||||
super(new JacksonJsonEncoderWithCustomization());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void canEncode() throws Exception {
|
||||
// Not Testing, covered under JacksonJsonEncoderTests
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void encode() throws Exception {
|
||||
Flux<MyCustomizedEncoderBean> input = Flux.just(
|
||||
new MyCustomizedEncoderBean(MyCustomEncoderEnum.VAL1),
|
||||
new MyCustomizedEncoderBean(MyCustomEncoderEnum.VAL2)
|
||||
);
|
||||
|
||||
testEncodeAll(input, ResolvableType.forClass(MyCustomizedEncoderBean.class), APPLICATION_NDJSON, null, step -> step
|
||||
.consumeNextWith(expectString("{\"property\":\"Value1\"}\n"))
|
||||
.consumeNextWith(expectString("{\"property\":\"Value2\"}\n"))
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeNonStream() {
|
||||
Flux<MyCustomizedEncoderBean> input = Flux.just(
|
||||
new MyCustomizedEncoderBean(MyCustomEncoderEnum.VAL1),
|
||||
new MyCustomizedEncoderBean(MyCustomEncoderEnum.VAL2)
|
||||
);
|
||||
|
||||
testEncode(input, MyCustomizedEncoderBean.class, step -> step
|
||||
.consumeNextWith(expectString("[{\"property\":\"Value1\"}").andThen(DataBufferUtils::release))
|
||||
.consumeNextWith(expectString(",{\"property\":\"Value2\"}").andThen(DataBufferUtils::release))
|
||||
.consumeNextWith(expectString("]").andThen(DataBufferUtils::release))
|
||||
.verifyComplete());
|
||||
}
|
||||
|
||||
|
||||
private static class MyCustomizedEncoderBean {
|
||||
|
||||
private MyCustomEncoderEnum property;
|
||||
|
||||
public MyCustomizedEncoderBean(MyCustomEncoderEnum property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public MyCustomEncoderEnum getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setProperty(MyCustomEncoderEnum property) {
|
||||
this.property = property;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum MyCustomEncoderEnum {
|
||||
VAL1,
|
||||
VAL2;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this == VAL1 ? "Value1" : "Value2";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class JacksonJsonEncoderWithCustomization extends JacksonJsonEncoder {
|
||||
|
||||
@Override
|
||||
protected ObjectWriter customizeWriter(
|
||||
ObjectWriter writer, MimeType mimeType, ResolvableType elementType, Map<String, Object> hints) {
|
||||
|
||||
return writer.with(EnumFeature.WRITE_ENUMS_USING_TO_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
import tools.jackson.databind.deser.std.StdDeserializer;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.CodecException;
|
||||
import org.springframework.core.codec.DecodingException;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.testfixture.codec.AbstractDecoderTests;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.JacksonViewBean.MyJacksonView1;
|
||||
import org.springframework.http.codec.json.JacksonViewBean.MyJacksonView3;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.web.testfixture.xml.Pojo;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_NDJSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_XML;
|
||||
import static org.springframework.http.codec.JacksonCodecSupport.JSON_VIEW_HINT;
|
||||
|
||||
/**
|
||||
* Tests for {@link JacksonJsonDecoder}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class JacksonJsonDecoderTests extends AbstractDecoderTests<JacksonJsonDecoder> {
|
||||
|
||||
private final Pojo pojo1 = new Pojo("f1", "b1");
|
||||
|
||||
private final Pojo pojo2 = new Pojo("f2", "b2");
|
||||
|
||||
|
||||
public JacksonJsonDecoderTests() {
|
||||
super(new JacksonJsonDecoder(JsonMapper.builder().build()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void canDecode() {
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), APPLICATION_JSON)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), APPLICATION_NDJSON)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), null)).isTrue();
|
||||
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(String.class), null)).isFalse();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), APPLICATION_XML)).isFalse();
|
||||
assertThat(this.decoder.canDecode(ResolvableType.forClass(Pojo.class),
|
||||
new MediaType("application", "json", StandardCharsets.UTF_8))).isTrue();
|
||||
assertThat(this.decoder.canDecode(ResolvableType.forClass(Pojo.class),
|
||||
new MediaType("application", "json", StandardCharsets.US_ASCII))).isTrue();
|
||||
assertThat(this.decoder.canDecode(ResolvableType.forClass(Pojo.class),
|
||||
new MediaType("application", "json", StandardCharsets.ISO_8859_1))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void canDecodeWithObjectMapperRegistrationForType() {
|
||||
MediaType halJsonMediaType = MediaType.parseMediaType("application/hal+json");
|
||||
MediaType halFormsJsonMediaType = MediaType.parseMediaType("application/prs.hal-forms+json");
|
||||
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), halJsonMediaType)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), MediaType.APPLICATION_JSON)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), halFormsJsonMediaType)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Map.class), MediaType.APPLICATION_JSON)).isTrue();
|
||||
|
||||
decoder.registerObjectMappersForType(Pojo.class, map -> {
|
||||
map.put(halJsonMediaType, new ObjectMapper());
|
||||
map.put(MediaType.APPLICATION_JSON, new ObjectMapper());
|
||||
});
|
||||
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), halJsonMediaType)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), MediaType.APPLICATION_JSON)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), halFormsJsonMediaType)).isFalse();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Map.class), MediaType.APPLICATION_JSON)).isTrue();
|
||||
|
||||
}
|
||||
|
||||
@Test // SPR-15866
|
||||
void canDecodeWithProvidedMimeType() {
|
||||
MimeType textJavascript = new MimeType("text", "javascript", StandardCharsets.UTF_8);
|
||||
JacksonJsonDecoder decoder = new JacksonJsonDecoder(new ObjectMapper(), textJavascript);
|
||||
|
||||
assertThat(decoder.getDecodableMimeTypes()).isEqualTo(Collections.singletonList(textJavascript));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void decodableMimeTypesIsImmutable() {
|
||||
MimeType textJavascript = new MimeType("text", "javascript", StandardCharsets.UTF_8);
|
||||
JacksonJsonDecoder decoder = new JacksonJsonDecoder(new ObjectMapper(), textJavascript);
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
decoder.getDecodableMimeTypes().add(new MimeType("text", "ecmascript")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodableMimeTypesWithObjectMapperRegistration() {
|
||||
MimeType mimeType1 = MediaType.parseMediaType("application/hal+json");
|
||||
MimeType mimeType2 = new MimeType("text", "javascript", StandardCharsets.UTF_8);
|
||||
|
||||
JacksonJsonDecoder decoder = new JacksonJsonDecoder(new ObjectMapper(), mimeType2);
|
||||
decoder.registerObjectMappersForType(Pojo.class, map -> map.put(mimeType1, new ObjectMapper()));
|
||||
|
||||
assertThat(decoder.getDecodableMimeTypes(ResolvableType.forClass(Pojo.class)))
|
||||
.containsExactly(mimeType1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void decode() {
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
stringBuffer("[{\"bar\":\"b1\",\"foo\":\"f1\"},"),
|
||||
stringBuffer("{\"bar\":\"b2\",\"foo\":\"f2\"}]"));
|
||||
|
||||
testDecodeAll(input, Pojo.class, step -> step
|
||||
.expectNext(pojo1)
|
||||
.expectNext(pojo2)
|
||||
.verifyComplete());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void decodeToMono() {
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
stringBuffer("[{\"bar\":\"b1\",\"foo\":\"f1\"},"),
|
||||
stringBuffer("{\"bar\":\"b2\",\"foo\":\"f2\"}]"));
|
||||
|
||||
ResolvableType elementType = ResolvableType.forClassWithGenerics(List.class, Pojo.class);
|
||||
|
||||
testDecodeToMonoAll(input, elementType, step -> step
|
||||
.expectNext(Arrays.asList(new Pojo("f1", "b1"), new Pojo("f2", "b2")))
|
||||
.expectComplete()
|
||||
.verify(), null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeToFluxWithListElements() {
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
stringBuffer("[{\"bar\":\"b1\",\"foo\":\"f1\"},{\"bar\":\"b2\",\"foo\":\"f2\"}]"),
|
||||
stringBuffer("[{\"bar\":\"b3\",\"foo\":\"f3\"},{\"bar\":\"b4\",\"foo\":\"f4\"}]"));
|
||||
|
||||
ResolvableType elementType = ResolvableType.forClassWithGenerics(List.class, Pojo.class);
|
||||
|
||||
testDecodeAll(input, elementType,
|
||||
step -> step
|
||||
.expectNext(List.of(pojo1, pojo2))
|
||||
.expectNext(List.of(new Pojo("f3", "b3"), new Pojo("f4", "b4")))
|
||||
.verifyComplete(),
|
||||
MimeTypeUtils.APPLICATION_JSON,
|
||||
Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeEmptyArrayToFlux() {
|
||||
Flux<DataBuffer> input = Flux.from(stringBuffer("[]"));
|
||||
|
||||
testDecode(input, Pojo.class, StepVerifier.LastStep::verifyComplete);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldLevelJsonView() {
|
||||
Flux<DataBuffer> input = Flux.from(stringBuffer(
|
||||
"{\"withView1\" : \"with\", \"withView2\" : \"with\", \"withoutView\" : \"without\"}"));
|
||||
|
||||
ResolvableType elementType = ResolvableType.forClass(JacksonViewBean.class);
|
||||
Map<String, Object> hints = Collections.singletonMap(JSON_VIEW_HINT, MyJacksonView1.class);
|
||||
|
||||
testDecode(input, elementType, step -> step
|
||||
.consumeNextWith(value -> {
|
||||
JacksonViewBean bean = (JacksonViewBean) value;
|
||||
assertThat(bean.getWithView1()).isEqualTo("with");
|
||||
assertThat(bean.getWithView2()).isNull();
|
||||
assertThat(bean.getWithoutView()).isNull();
|
||||
}), null, hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void classLevelJsonView() {
|
||||
Flux<DataBuffer> input = Flux.from(stringBuffer(
|
||||
"{\"withoutView\" : \"without\"}"));
|
||||
|
||||
ResolvableType elementType = ResolvableType.forClass(JacksonViewBean.class);
|
||||
Map<String, Object> hints = Collections.singletonMap(JSON_VIEW_HINT, MyJacksonView3.class);
|
||||
|
||||
testDecode(input, elementType, step -> step
|
||||
.consumeNextWith(value -> {
|
||||
JacksonViewBean bean = (JacksonViewBean) value;
|
||||
assertThat(bean.getWithoutView()).isEqualTo("without");
|
||||
assertThat(bean.getWithView1()).isNull();
|
||||
assertThat(bean.getWithView2()).isNull();
|
||||
})
|
||||
.verifyComplete(), null, hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidData() {
|
||||
Flux<DataBuffer> input = Flux.from(stringBuffer("{\"foofoo\": \"foofoo\", \"barbar\": \"barbar\""));
|
||||
testDecode(input, Pojo.class, step -> step.verifyError(DecodingException.class));
|
||||
}
|
||||
|
||||
@Test // gh-22042
|
||||
void decodeWithNullLiteral() {
|
||||
Flux<Object> result = this.decoder.decode(Flux.concat(stringBuffer("null")),
|
||||
ResolvableType.forType(Pojo.class), MediaType.APPLICATION_JSON, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(result).expectComplete().verify();
|
||||
}
|
||||
|
||||
@Test // gh-27511
|
||||
void noDefaultConstructor() {
|
||||
Flux<DataBuffer> input = Flux.from(stringBuffer("{\"property1\":\"foo\",\"property2\":\"bar\"}"));
|
||||
|
||||
testDecode(input, BeanWithNoDefaultConstructor.class, step -> step
|
||||
.consumeNextWith(o -> {
|
||||
assertThat(o.getProperty1()).isEqualTo("foo");
|
||||
assertThat(o.getProperty2()).isEqualTo("bar");
|
||||
})
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void codecException() {
|
||||
Flux<DataBuffer> input = Flux.from(stringBuffer("["));
|
||||
ResolvableType elementType = ResolvableType.forClass(BeanWithNoDefaultConstructor.class);
|
||||
Flux<Object> flux = new Jackson2JsonDecoder().decode(input, elementType, null, Collections.emptyMap());
|
||||
StepVerifier.create(flux).verifyError(CodecException.class);
|
||||
}
|
||||
|
||||
@Test // SPR-15975
|
||||
void customDeserializer() {
|
||||
Mono<DataBuffer> input = stringBuffer("{\"test\": 1}");
|
||||
|
||||
testDecode(input, TestObject.class, step -> step
|
||||
.consumeNextWith(o -> assertThat(o.getTest()).isEqualTo(1))
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bigDecimalFlux() {
|
||||
Flux<DataBuffer> input = stringBuffer("[ 1E+2 ]").flux();
|
||||
|
||||
testDecode(input, BigDecimal.class, step -> step
|
||||
.expectNext(new BigDecimal("1E+2"))
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void decodeNonUtf8Encoding() {
|
||||
Mono<DataBuffer> input = stringBuffer("{\"foo\":\"bar\"}", StandardCharsets.UTF_16);
|
||||
ResolvableType type = ResolvableType.forType(new ParameterizedTypeReference<Map<String, String>>() {});
|
||||
|
||||
testDecode(input, type, step -> step
|
||||
.assertNext(value -> assertThat((Map<String, String>) value).containsEntry("foo", "bar"))
|
||||
.verifyComplete(),
|
||||
MediaType.parseMediaType("application/json; charset=utf-16"),
|
||||
null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void decodeNonUnicode() {
|
||||
Flux<DataBuffer> input = Flux.concat(stringBuffer("{\"føø\":\"bår\"}", StandardCharsets.ISO_8859_1));
|
||||
ResolvableType type = ResolvableType.forType(new ParameterizedTypeReference<Map<String, String>>() {});
|
||||
|
||||
testDecode(input, type, step -> step
|
||||
.assertNext(o -> assertThat((Map<String, String>) o).containsEntry("føø", "bår"))
|
||||
.verifyComplete(),
|
||||
MediaType.parseMediaType("application/json; charset=iso-8859-1"),
|
||||
null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void decodeMonoNonUtf8Encoding() {
|
||||
Mono<DataBuffer> input = stringBuffer("{\"foo\":\"bar\"}", StandardCharsets.UTF_16);
|
||||
ResolvableType type = ResolvableType.forType(new ParameterizedTypeReference<Map<String, String>>() {});
|
||||
|
||||
testDecodeToMono(input, type, step -> step
|
||||
.assertNext(value -> assertThat((Map<String, String>) value).containsEntry("foo", "bar"))
|
||||
.verifyComplete(),
|
||||
MediaType.parseMediaType("application/json; charset=utf-16"),
|
||||
null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void decodeAscii() {
|
||||
Flux<DataBuffer> input = Flux.concat(stringBuffer("{\"foo\":\"bar\"}", StandardCharsets.US_ASCII));
|
||||
ResolvableType type = ResolvableType.forType(new ParameterizedTypeReference<Map<String, String>>() {});
|
||||
|
||||
testDecode(input, type, step -> step
|
||||
.assertNext(value -> assertThat((Map<String, String>) value).containsEntry("foo", "bar"))
|
||||
.verifyComplete(),
|
||||
MediaType.parseMediaType("application/json; charset=us-ascii"),
|
||||
null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void cancelWhileDecoding() {
|
||||
Flux<DataBuffer> input = Flux.just(
|
||||
stringBuffer("[{\"bar\":\"b1\",\"foo\":\"f1\"},").block(),
|
||||
stringBuffer("{\"bar\":\"b2\",\"foo\":\"f2\"}]").block());
|
||||
|
||||
testDecodeCancel(input, ResolvableType.forClass(Pojo.class), null, null);
|
||||
}
|
||||
|
||||
|
||||
private Mono<DataBuffer> stringBuffer(String value) {
|
||||
return stringBuffer(value, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private Mono<DataBuffer> stringBuffer(String value, Charset charset) {
|
||||
return Mono.defer(() -> {
|
||||
byte[] bytes = value.getBytes(charset);
|
||||
DataBuffer buffer = this.bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
return Mono.just(buffer);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class BeanWithNoDefaultConstructor {
|
||||
|
||||
private final String property1;
|
||||
|
||||
private final String property2;
|
||||
|
||||
public BeanWithNoDefaultConstructor(String property1, String property2) {
|
||||
this.property1 = property1;
|
||||
this.property2 = property2;
|
||||
}
|
||||
|
||||
public String getProperty1() {
|
||||
return this.property1;
|
||||
}
|
||||
|
||||
public String getProperty2() {
|
||||
return this.property2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsonDeserialize(using = Deserializer.class)
|
||||
private static class TestObject {
|
||||
|
||||
private int test;
|
||||
|
||||
public int getTest() {
|
||||
return this.test;
|
||||
}
|
||||
public void setTest(int test) {
|
||||
this.test = test;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class Deserializer extends StdDeserializer<TestObject> {
|
||||
|
||||
Deserializer() {
|
||||
super(TestObject.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestObject deserialize(JsonParser p, DeserializationContext ctxt) {
|
||||
JsonNode node = p.readValueAsTree();
|
||||
TestObject result = new TestObject();
|
||||
result.setTest(node.get("test").asInt());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec.json;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.SerializationFeature;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.testfixture.codec.AbstractEncoderTests;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.JacksonViewBean.MyJacksonView1;
|
||||
import org.springframework.http.codec.json.JacksonViewBean.MyJacksonView3;
|
||||
import org.springframework.http.converter.json.MappingJacksonValue;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.web.testfixture.xml.Pojo;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_NDJSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
|
||||
import static org.springframework.http.MediaType.APPLICATION_XML;
|
||||
import static org.springframework.http.codec.JacksonCodecSupport.JSON_VIEW_HINT;
|
||||
|
||||
/**
|
||||
* Tests for {@link JacksonJsonEncoder}.
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class JacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> {
|
||||
|
||||
public JacksonJsonEncoderTests() {
|
||||
super(new JacksonJsonEncoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void canEncode() {
|
||||
ResolvableType pojoType = ResolvableType.forClass(Pojo.class);
|
||||
assertThat(this.encoder.canEncode(pojoType, APPLICATION_JSON)).isTrue();
|
||||
assertThat(this.encoder.canEncode(pojoType, APPLICATION_NDJSON)).isTrue();
|
||||
assertThat(this.encoder.canEncode(pojoType, null)).isTrue();
|
||||
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class),
|
||||
new MediaType("application", "json", StandardCharsets.UTF_8))).isTrue();
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class),
|
||||
new MediaType("application", "json", StandardCharsets.US_ASCII))).isTrue();
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class),
|
||||
new MediaType("application", "json", StandardCharsets.ISO_8859_1))).isFalse();
|
||||
|
||||
// SPR-15464
|
||||
assertThat(this.encoder.canEncode(ResolvableType.NONE, null)).isTrue();
|
||||
|
||||
// SPR-15910
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(Object.class), APPLICATION_OCTET_STREAM)).isFalse();
|
||||
|
||||
assertThatThrownBy(() -> this.encoder.canEncode(ResolvableType.forClass(MappingJacksonValue.class), APPLICATION_JSON))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void encode() throws Exception {
|
||||
Flux<Object> input = Flux.just(new Pojo("foo", "bar"),
|
||||
new Pojo("foofoo", "barbar"),
|
||||
new Pojo("foofoofoo", "barbarbar"));
|
||||
|
||||
testEncodeAll(input, ResolvableType.forClass(Pojo.class), APPLICATION_NDJSON, null, step -> step
|
||||
.consumeNextWith(expectString("{\"bar\":\"bar\",\"foo\":\"foo\"}\n"))
|
||||
.consumeNextWith(expectString("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}\n"))
|
||||
.consumeNextWith(expectString("{\"bar\":\"barbarbar\",\"foo\":\"foofoofoo\"}\n"))
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
@Test // SPR-15866
|
||||
public void canEncodeWithCustomMimeType() {
|
||||
MimeType textJavascript = new MimeType("text", "javascript", StandardCharsets.UTF_8);
|
||||
JacksonJsonEncoder encoder = new JacksonJsonEncoder(new ObjectMapper(), textJavascript);
|
||||
|
||||
assertThat(encoder.getEncodableMimeTypes()).isEqualTo(Collections.singletonList(textJavascript));
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodableMimeTypesIsImmutable() {
|
||||
MimeType textJavascript = new MimeType("text", "javascript", StandardCharsets.UTF_8);
|
||||
JacksonJsonEncoder encoder = new JacksonJsonEncoder(new ObjectMapper(), textJavascript);
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
encoder.getEncodableMimeTypes().add(new MimeType("text", "ecmascript")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void canNotEncode() {
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(String.class), null)).isFalse();
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), APPLICATION_XML)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeNonStream() {
|
||||
Flux<Pojo> input = Flux.just(
|
||||
new Pojo("foo", "bar"),
|
||||
new Pojo("foofoo", "barbar"),
|
||||
new Pojo("foofoofoo", "barbarbar")
|
||||
);
|
||||
|
||||
testEncode(input, Pojo.class, step -> step
|
||||
.consumeNextWith(expectString("[{\"bar\":\"bar\",\"foo\":\"foo\"}"))
|
||||
.consumeNextWith(expectString(",{\"bar\":\"barbar\",\"foo\":\"foofoo\"}"))
|
||||
.consumeNextWith(expectString(",{\"bar\":\"barbarbar\",\"foo\":\"foofoofoo\"}"))
|
||||
.consumeNextWith(expectString("]"))
|
||||
.verifyComplete());
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeNonStreamEmpty() {
|
||||
testEncode(Flux.empty(), Pojo.class, step -> step
|
||||
.consumeNextWith(expectString("["))
|
||||
.consumeNextWith(expectString("]"))
|
||||
.verifyComplete());
|
||||
}
|
||||
|
||||
@Test // gh-29038
|
||||
void encodeNonStreamWithErrorAsFirstSignal() {
|
||||
String message = "I'm a teapot";
|
||||
Flux<Object> input = Flux.error(new IllegalStateException(message));
|
||||
|
||||
Flux<DataBuffer> output = this.encoder.encode(
|
||||
input, this.bufferFactory, ResolvableType.forClass(Pojo.class), null, null);
|
||||
|
||||
StepVerifier.create(output).expectErrorMessage(message).verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeWithType() {
|
||||
Flux<ParentClass> input = Flux.just(new Foo(), new Bar());
|
||||
|
||||
testEncode(input, ParentClass.class, step -> step
|
||||
.consumeNextWith(expectString("[{\"type\":\"foo\"}"))
|
||||
.consumeNextWith(expectString(",{\"type\":\"bar\"}"))
|
||||
.consumeNextWith(expectString("]"))
|
||||
.verifyComplete());
|
||||
}
|
||||
|
||||
|
||||
@Test // SPR-15727
|
||||
public void encodeStreamWithCustomStreamingType() {
|
||||
MediaType fooMediaType = new MediaType("application", "foo");
|
||||
MediaType barMediaType = new MediaType("application", "bar");
|
||||
this.encoder.setStreamingMediaTypes(Arrays.asList(fooMediaType, barMediaType));
|
||||
Flux<Pojo> input = Flux.just(
|
||||
new Pojo("foo", "bar"),
|
||||
new Pojo("foofoo", "barbar"),
|
||||
new Pojo("foofoofoo", "barbarbar")
|
||||
);
|
||||
|
||||
testEncode(input, ResolvableType.forClass(Pojo.class), barMediaType, null, step -> step
|
||||
.consumeNextWith(expectString("{\"bar\":\"bar\",\"foo\":\"foo\"}\n"))
|
||||
.consumeNextWith(expectString("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}\n"))
|
||||
.consumeNextWith(expectString("{\"bar\":\"barbarbar\",\"foo\":\"foofoofoo\"}\n"))
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldLevelJsonView() {
|
||||
JacksonViewBean bean = new JacksonViewBean();
|
||||
bean.setWithView1("with");
|
||||
bean.setWithView2("with");
|
||||
bean.setWithoutView("without");
|
||||
Mono<JacksonViewBean> input = Mono.just(bean);
|
||||
|
||||
ResolvableType type = ResolvableType.forClass(JacksonViewBean.class);
|
||||
Map<String, Object> hints = singletonMap(JSON_VIEW_HINT, MyJacksonView1.class);
|
||||
|
||||
testEncode(input, type, null, hints, step -> step
|
||||
.consumeNextWith(expectString("{\"withView1\":\"with\"}"))
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void classLevelJsonView() {
|
||||
JacksonViewBean bean = new JacksonViewBean();
|
||||
bean.setWithView1("with");
|
||||
bean.setWithView2("with");
|
||||
bean.setWithoutView("without");
|
||||
Mono<JacksonViewBean> input = Mono.just(bean);
|
||||
|
||||
ResolvableType type = ResolvableType.forClass(JacksonViewBean.class);
|
||||
Map<String, Object> hints = singletonMap(JSON_VIEW_HINT, MyJacksonView3.class);
|
||||
|
||||
testEncode(input, type, null, hints, step -> step
|
||||
.consumeNextWith(expectString("{\"withoutView\":\"without\"}"))
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
@Test // gh-22771
|
||||
public void encodeWithFlushAfterWriteOff() {
|
||||
ObjectMapper mapper = JsonMapper.builder().configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, false).build();
|
||||
JacksonJsonEncoder encoder = new JacksonJsonEncoder(mapper);
|
||||
|
||||
Flux<DataBuffer> result = encoder.encode(Flux.just(new Pojo("foo", "bar")), this.bufferFactory,
|
||||
ResolvableType.forClass(Pojo.class), MimeTypeUtils.APPLICATION_JSON, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(expectString("[{\"bar\":\"bar\",\"foo\":\"foo\"}"))
|
||||
.consumeNextWith(expectString("]"))
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeAscii() {
|
||||
Mono<Object> input = Mono.just(new Pojo("foo", "bar"));
|
||||
MimeType mimeType = new MimeType("application", "json", StandardCharsets.US_ASCII);
|
||||
|
||||
testEncode(input, ResolvableType.forClass(Pojo.class), mimeType, null, step -> step
|
||||
.consumeNextWith(expectString("{\"bar\":\"bar\",\"foo\":\"foo\"}"))
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
private static class ParentClass {
|
||||
}
|
||||
|
||||
@JsonTypeName("foo")
|
||||
private static class Foo extends ParentClass {
|
||||
}
|
||||
|
||||
@JsonTypeName("bar")
|
||||
private static class Bar extends ParentClass {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec.smile;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import tools.jackson.dataformat.smile.SmileMapper;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.testfixture.codec.AbstractDecoderTests;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.web.testfixture.xml.Pojo;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
|
||||
/**
|
||||
* Tests for {@link JacksonSmileDecoder}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class JacksonSmileDecoderTests extends AbstractDecoderTests<JacksonSmileDecoder> {
|
||||
|
||||
private static final MimeType SMILE_MIME_TYPE = new MimeType("application", "x-jackson-smile");
|
||||
private static final MimeType STREAM_SMILE_MIME_TYPE = new MimeType("application", "stream+x-jackson-smile");
|
||||
|
||||
private Pojo pojo1 = new Pojo("f1", "b1");
|
||||
|
||||
private Pojo pojo2 = new Pojo("f2", "b2");
|
||||
|
||||
private SmileMapper mapper = SmileMapper.builder().build();
|
||||
|
||||
public JacksonSmileDecoderTests() {
|
||||
super(new JacksonSmileDecoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void canDecode() {
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), SMILE_MIME_TYPE)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), STREAM_SMILE_MIME_TYPE)).isTrue();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), null)).isTrue();
|
||||
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(String.class), null)).isFalse();
|
||||
assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), APPLICATION_JSON)).isFalse();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void decode() {
|
||||
Flux<DataBuffer> input = Flux.just(this.pojo1, this.pojo2)
|
||||
.map(this::writeObject)
|
||||
.flatMap(this::dataBuffer);
|
||||
|
||||
testDecodeAll(input, Pojo.class, step -> step
|
||||
.expectNext(pojo1)
|
||||
.expectNext(pojo2)
|
||||
.verifyComplete());
|
||||
|
||||
}
|
||||
|
||||
private byte[] writeObject(Object o) {
|
||||
return this.mapper.writer().writeValueAsBytes(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void decodeToMono() {
|
||||
List<Pojo> expected = Arrays.asList(pojo1, pojo2);
|
||||
|
||||
Flux<DataBuffer> input = Flux.just(expected)
|
||||
.map(this::writeObject)
|
||||
.flatMap(this::dataBuffer);
|
||||
|
||||
ResolvableType elementType = ResolvableType.forClassWithGenerics(List.class, Pojo.class);
|
||||
testDecodeToMono(input, elementType, step -> step
|
||||
.expectNext(expected)
|
||||
.expectComplete()
|
||||
.verify(), null, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec.smile;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import tools.jackson.databind.MappingIterator;
|
||||
import tools.jackson.dataformat.smile.SmileMapper;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.testfixture.codec.AbstractEncoderTests;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.http.codec.json.Jackson2SmileEncoder;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.web.testfixture.xml.Pojo;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.core.io.buffer.DataBufferUtils.release;
|
||||
import static org.springframework.http.MediaType.APPLICATION_XML;
|
||||
|
||||
/**
|
||||
* Tests for {@link JacksonSmileEncoder}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class JacksonSmileEncoderTests extends AbstractEncoderTests<JacksonSmileEncoder> {
|
||||
|
||||
private static final MimeType SMILE_MIME_TYPE = new MimeType("application", "x-jackson-smile");
|
||||
private static final MimeType STREAM_SMILE_MIME_TYPE = new MimeType("application", "stream+x-jackson-smile");
|
||||
|
||||
private final Jackson2SmileEncoder encoder = new Jackson2SmileEncoder();
|
||||
|
||||
private final SmileMapper mapper = SmileMapper.builder().build();
|
||||
|
||||
public JacksonSmileEncoderTests() {
|
||||
super(new JacksonSmileEncoder());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void canEncode() {
|
||||
ResolvableType pojoType = ResolvableType.forClass(Pojo.class);
|
||||
assertThat(this.encoder.canEncode(pojoType, SMILE_MIME_TYPE)).isTrue();
|
||||
assertThat(this.encoder.canEncode(pojoType, STREAM_SMILE_MIME_TYPE)).isTrue();
|
||||
assertThat(this.encoder.canEncode(pojoType, null)).isTrue();
|
||||
|
||||
// SPR-15464
|
||||
assertThat(this.encoder.canEncode(ResolvableType.NONE, null)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void canNotEncode() {
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(String.class), null)).isFalse();
|
||||
assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), APPLICATION_XML)).isFalse();
|
||||
|
||||
ResolvableType sseType = ResolvableType.forClass(ServerSentEvent.class);
|
||||
assertThat(this.encoder.canEncode(sseType, SMILE_MIME_TYPE)).isFalse();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
protected void encode() {
|
||||
List<Pojo> list = Arrays.asList(
|
||||
new Pojo("foo", "bar"),
|
||||
new Pojo("foofoo", "barbar"),
|
||||
new Pojo("foofoofoo", "barbarbar"));
|
||||
|
||||
Flux<Pojo> input = Flux.fromIterable(list);
|
||||
|
||||
testEncode(input, Pojo.class, step -> step
|
||||
.consumeNextWith(dataBuffer -> {
|
||||
try {
|
||||
Object actual = this.mapper.reader().forType(List.class)
|
||||
.readValue(dataBuffer.asInputStream());
|
||||
assertThat(actual).isEqualTo(list);
|
||||
}
|
||||
finally {
|
||||
release(dataBuffer);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeError() {
|
||||
Mono<Pojo> input = Mono.error(new InputException());
|
||||
testEncode(input, Pojo.class, step -> step.expectError(InputException.class).verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeAsStream() {
|
||||
Pojo pojo1 = new Pojo("foo", "bar");
|
||||
Pojo pojo2 = new Pojo("foofoo", "barbar");
|
||||
Pojo pojo3 = new Pojo("foofoofoo", "barbarbar");
|
||||
Flux<Pojo> input = Flux.just(pojo1, pojo2, pojo3);
|
||||
ResolvableType type = ResolvableType.forClass(Pojo.class);
|
||||
|
||||
Flux<DataBuffer> result = this.encoder
|
||||
.encode(input, bufferFactory, type, STREAM_SMILE_MIME_TYPE, null);
|
||||
|
||||
Mono<MappingIterator<Pojo>> joined = DataBufferUtils.join(result)
|
||||
.map(buffer -> this.mapper.reader().forType(Pojo.class).readValues(buffer.asInputStream(true)));
|
||||
|
||||
StepVerifier.create(joined)
|
||||
.assertNext(iter -> assertThat(iter).toIterable().contains(pojo1, pojo2, pojo3))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@@ -55,11 +54,8 @@ import org.springframework.http.codec.ResourceHttpMessageWriter;
|
||||
import org.springframework.http.codec.ServerSentEventHttpMessageReader;
|
||||
import org.springframework.http.codec.cbor.KotlinSerializationCborDecoder;
|
||||
import org.springframework.http.codec.cbor.KotlinSerializationCborEncoder;
|
||||
import org.springframework.http.codec.json.Jackson2CodecSupport;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.codec.json.Jackson2SmileDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2SmileEncoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
|
||||
@@ -70,6 +66,8 @@ import org.springframework.http.codec.protobuf.KotlinSerializationProtobufDecode
|
||||
import org.springframework.http.codec.protobuf.KotlinSerializationProtobufEncoder;
|
||||
import org.springframework.http.codec.protobuf.ProtobufDecoder;
|
||||
import org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter;
|
||||
import org.springframework.http.codec.smile.JacksonSmileDecoder;
|
||||
import org.springframework.http.codec.smile.JacksonSmileEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
@@ -81,6 +79,7 @@ import static org.springframework.core.ResolvableType.forClass;
|
||||
* Tests for {@link ClientCodecConfigurer}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class ClientCodecConfigurerTests {
|
||||
|
||||
@@ -107,8 +106,8 @@ class ClientCodecConfigurerTests {
|
||||
assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageReader.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2JsonDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2SmileDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonJsonDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonSmileDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jaxb2XmlDecoder.class);
|
||||
assertSseReader(readers);
|
||||
assertStringDecoder(getNextDecoder(readers), false);
|
||||
@@ -130,55 +129,33 @@ class ClientCodecConfigurerTests {
|
||||
assertThat(writers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartHttpMessageWriter.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jackson2JsonEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jackson2SmileEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonJsonEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonSmileEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jaxb2XmlEncoder.class);
|
||||
assertStringEncoder(getNextEncoder(writers), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jackson2CodecCustomization() {
|
||||
Jackson2JsonDecoder decoder = new Jackson2JsonDecoder();
|
||||
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
|
||||
this.configurer.defaultCodecs().jackson2JsonDecoder(decoder);
|
||||
this.configurer.defaultCodecs().jackson2JsonEncoder(encoder);
|
||||
void jacksonCodecCustomization() {
|
||||
JacksonJsonDecoder decoder = new JacksonJsonDecoder();
|
||||
JacksonJsonEncoder encoder = new JacksonJsonEncoder();
|
||||
this.configurer.defaultCodecs().jacksonJsonDecoder(decoder);
|
||||
this.configurer.defaultCodecs().jacksonJsonEncoder(encoder);
|
||||
|
||||
List<HttpMessageReader<?>> readers = this.configurer.getReaders();
|
||||
Jackson2JsonDecoder actualDecoder = findCodec(readers, Jackson2JsonDecoder.class);
|
||||
JacksonJsonDecoder actualDecoder = findCodec(readers, JacksonJsonDecoder.class);
|
||||
assertThat(actualDecoder).isSameAs(decoder);
|
||||
assertThat(findCodec(readers, ServerSentEventHttpMessageReader.class).getDecoder()).isSameAs(decoder);
|
||||
|
||||
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
|
||||
Jackson2JsonEncoder actualEncoder = findCodec(writers, Jackson2JsonEncoder.class);
|
||||
JacksonJsonEncoder actualEncoder = findCodec(writers, JacksonJsonEncoder.class);
|
||||
assertThat(actualEncoder).isSameAs(encoder);
|
||||
|
||||
MultipartHttpMessageWriter multipartWriter = findCodec(writers, MultipartHttpMessageWriter.class);
|
||||
actualEncoder = findCodec(multipartWriter.getPartWriters(), Jackson2JsonEncoder.class);
|
||||
actualEncoder = findCodec(multipartWriter.getPartWriters(), JacksonJsonEncoder.class);
|
||||
assertThat(actualEncoder).isSameAs(encoder);
|
||||
}
|
||||
|
||||
@Test
|
||||
void objectMapperCustomization() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
this.configurer.defaultCodecs().configureDefaultCodec(codec -> {
|
||||
if (codec instanceof Jackson2CodecSupport) {
|
||||
((Jackson2CodecSupport) codec).setObjectMapper(objectMapper);
|
||||
}
|
||||
});
|
||||
|
||||
List<HttpMessageReader<?>> readers = this.configurer.getReaders();
|
||||
Jackson2JsonDecoder actualDecoder = findCodec(readers, Jackson2JsonDecoder.class);
|
||||
assertThat(actualDecoder.getObjectMapper()).isSameAs(objectMapper);
|
||||
|
||||
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
|
||||
Jackson2JsonEncoder actualEncoder = findCodec(writers, Jackson2JsonEncoder.class);
|
||||
assertThat(actualEncoder.getObjectMapper()).isSameAs(objectMapper);
|
||||
|
||||
MultipartHttpMessageWriter multipartWriter = findCodec(writers, MultipartHttpMessageWriter.class);
|
||||
actualEncoder = findCodec(multipartWriter.getPartWriters(), Jackson2JsonEncoder.class);
|
||||
assertThat(actualEncoder.getObjectMapper()).isSameAs(objectMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxInMemorySize() {
|
||||
int size = 99;
|
||||
@@ -198,13 +175,13 @@ class ClientCodecConfigurerTests {
|
||||
assertThat(((PartEventHttpMessageReader) nextReader(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((KotlinSerializationCborDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((KotlinSerializationProtobufDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((Jackson2JsonDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((Jackson2SmileDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((JacksonJsonDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((JacksonSmileDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((Jaxb2XmlDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
|
||||
ServerSentEventHttpMessageReader reader = (ServerSentEventHttpMessageReader) nextReader(readers);
|
||||
assertThat(reader.getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((Jackson2JsonDecoder) reader.getDecoder()).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((JacksonJsonDecoder) reader.getDecoder()).getMaxInMemorySize()).isEqualTo(size);
|
||||
|
||||
assertThat(((StringDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
}
|
||||
@@ -226,9 +203,9 @@ class ClientCodecConfigurerTests {
|
||||
void clonedConfigurer() {
|
||||
ClientCodecConfigurer clone = this.configurer.clone();
|
||||
|
||||
Jackson2JsonDecoder jackson2Decoder = new Jackson2JsonDecoder();
|
||||
clone.defaultCodecs().serverSentEventDecoder(jackson2Decoder);
|
||||
clone.defaultCodecs().multipartCodecs().encoder(new Jackson2SmileEncoder());
|
||||
JacksonJsonDecoder jacksonDecoder = new JacksonJsonDecoder();
|
||||
clone.defaultCodecs().serverSentEventDecoder(jacksonDecoder);
|
||||
clone.defaultCodecs().multipartCodecs().encoder(new JacksonSmileEncoder());
|
||||
clone.defaultCodecs().multipartCodecs().writer(new ResourceHttpMessageWriter());
|
||||
|
||||
// Clone has the customizations
|
||||
@@ -236,7 +213,7 @@ class ClientCodecConfigurerTests {
|
||||
Decoder<?> sseDecoder = findCodec(clone.getReaders(), ServerSentEventHttpMessageReader.class).getDecoder();
|
||||
List<HttpMessageWriter<?>> writers = findCodec(clone.getWriters(), MultipartHttpMessageWriter.class).getPartWriters();
|
||||
|
||||
assertThat(sseDecoder).isSameAs(jackson2Decoder);
|
||||
assertThat(sseDecoder).isSameAs(jacksonDecoder);
|
||||
assertThat(writers).hasSize(2);
|
||||
|
||||
// Original does not have the customizations
|
||||
@@ -244,7 +221,7 @@ class ClientCodecConfigurerTests {
|
||||
sseDecoder = findCodec(this.configurer.getReaders(), ServerSentEventHttpMessageReader.class).getDecoder();
|
||||
writers = findCodec(this.configurer.getWriters(), MultipartHttpMessageWriter.class).getPartWriters();
|
||||
|
||||
assertThat(sseDecoder).isNotSameAs(jackson2Decoder);
|
||||
assertThat(sseDecoder).isNotSameAs(jacksonDecoder);
|
||||
assertThat(writers).hasSize(16);
|
||||
}
|
||||
|
||||
@@ -264,7 +241,7 @@ class ClientCodecConfigurerTests {
|
||||
ClientCodecConfigurer clone = this.configurer.clone();
|
||||
|
||||
this.configurer.registerDefaults(false);
|
||||
this.configurer.customCodecs().register(new Jackson2JsonEncoder());
|
||||
this.configurer.customCodecs().register(new JacksonJsonEncoder());
|
||||
|
||||
List<HttpMessageWriter<?>> writers =
|
||||
findCodec(clone.getWriters(), MultipartHttpMessageWriter.class).getPartWriters();
|
||||
@@ -332,7 +309,7 @@ class ClientCodecConfigurerTests {
|
||||
assertThat(reader.getClass()).isEqualTo(ServerSentEventHttpMessageReader.class);
|
||||
Decoder<?> decoder = ((ServerSentEventHttpMessageReader) reader).getDecoder();
|
||||
assertThat(decoder).isNotNull();
|
||||
assertThat(decoder.getClass()).isEqualTo(Jackson2JsonDecoder.class);
|
||||
assertThat(decoder.getClass()).isEqualTo(JacksonJsonDecoder.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,10 +49,8 @@ import org.springframework.http.codec.ServerSentEventHttpMessageReader;
|
||||
import org.springframework.http.codec.ServerSentEventHttpMessageWriter;
|
||||
import org.springframework.http.codec.cbor.KotlinSerializationCborDecoder;
|
||||
import org.springframework.http.codec.cbor.KotlinSerializationCborEncoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.codec.json.Jackson2SmileDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2SmileEncoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
|
||||
@@ -64,6 +62,8 @@ import org.springframework.http.codec.protobuf.KotlinSerializationProtobufEncode
|
||||
import org.springframework.http.codec.protobuf.ProtobufDecoder;
|
||||
import org.springframework.http.codec.protobuf.ProtobufEncoder;
|
||||
import org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter;
|
||||
import org.springframework.http.codec.smile.JacksonSmileDecoder;
|
||||
import org.springframework.http.codec.smile.JacksonSmileEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
@@ -102,8 +102,8 @@ class CodecConfigurerTests {
|
||||
assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageReader.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2JsonDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2SmileDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonJsonDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonSmileDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jaxb2XmlDecoder.class);
|
||||
assertStringDecoder(getNextDecoder(readers), false);
|
||||
}
|
||||
@@ -124,8 +124,8 @@ class CodecConfigurerTests {
|
||||
assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(PartHttpMessageWriter.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jackson2JsonEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jackson2SmileEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonJsonEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonSmileEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jaxb2XmlEncoder.class);
|
||||
assertStringEncoder(getNextEncoder(writers), false);
|
||||
}
|
||||
@@ -170,8 +170,8 @@ class CodecConfigurerTests {
|
||||
assertThat(readers.get(this.index.getAndIncrement())).isSameAs(customReader2);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2JsonDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2SmileDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonJsonDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonSmileDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jaxb2XmlDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(StringDecoder.class);
|
||||
}
|
||||
@@ -215,8 +215,8 @@ class CodecConfigurerTests {
|
||||
assertThat(writers.get(this.index.getAndIncrement())).isSameAs(customWriter2);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jackson2JsonEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jackson2SmileEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonJsonEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonSmileEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jaxb2XmlEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(CharSequenceEncoder.class);
|
||||
}
|
||||
@@ -285,19 +285,19 @@ class CodecConfigurerTests {
|
||||
|
||||
@Test
|
||||
void encoderDecoderOverrides() {
|
||||
Jackson2JsonDecoder jacksonDecoder = new Jackson2JsonDecoder();
|
||||
Jackson2JsonEncoder jacksonEncoder = new Jackson2JsonEncoder();
|
||||
Jackson2SmileDecoder smileDecoder = new Jackson2SmileDecoder();
|
||||
Jackson2SmileEncoder smileEncoder = new Jackson2SmileEncoder();
|
||||
JacksonJsonDecoder jacksonDecoder = new JacksonJsonDecoder();
|
||||
JacksonJsonEncoder jacksonEncoder = new JacksonJsonEncoder();
|
||||
JacksonSmileDecoder smileDecoder = new JacksonSmileDecoder();
|
||||
JacksonSmileEncoder smileEncoder = new JacksonSmileEncoder();
|
||||
ProtobufDecoder protobufDecoder = new ProtobufDecoder(ExtensionRegistry.newInstance());
|
||||
ProtobufEncoder protobufEncoder = new ProtobufEncoder();
|
||||
Jaxb2XmlEncoder jaxb2Encoder = new Jaxb2XmlEncoder();
|
||||
Jaxb2XmlDecoder jaxb2Decoder = new Jaxb2XmlDecoder();
|
||||
|
||||
this.configurer.defaultCodecs().jackson2JsonDecoder(jacksonDecoder);
|
||||
this.configurer.defaultCodecs().jackson2JsonEncoder(jacksonEncoder);
|
||||
this.configurer.defaultCodecs().jackson2SmileDecoder(smileDecoder);
|
||||
this.configurer.defaultCodecs().jackson2SmileEncoder(smileEncoder);
|
||||
this.configurer.defaultCodecs().jacksonJsonDecoder(jacksonDecoder);
|
||||
this.configurer.defaultCodecs().jacksonJsonEncoder(jacksonEncoder);
|
||||
this.configurer.defaultCodecs().jacksonSmileDecoder(smileDecoder);
|
||||
this.configurer.defaultCodecs().jacksonSmileEncoder(smileEncoder);
|
||||
this.configurer.defaultCodecs().protobufDecoder(protobufDecoder);
|
||||
this.configurer.defaultCodecs().protobufEncoder(protobufEncoder);
|
||||
this.configurer.defaultCodecs().jaxb2Decoder(jaxb2Decoder);
|
||||
@@ -320,8 +320,8 @@ class CodecConfigurerTests {
|
||||
assertThat(this.configurer.getWriters()).isEmpty();
|
||||
|
||||
CodecConfigurer clone = this.configurer.clone();
|
||||
clone.customCodecs().register(new Jackson2JsonEncoder());
|
||||
clone.customCodecs().register(new Jackson2JsonDecoder());
|
||||
clone.customCodecs().register(new JacksonJsonEncoder());
|
||||
clone.customCodecs().register(new JacksonJsonDecoder());
|
||||
clone.customCodecs().register(new ServerSentEventHttpMessageReader());
|
||||
clone.customCodecs().register(new ServerSentEventHttpMessageWriter());
|
||||
|
||||
@@ -337,8 +337,8 @@ class CodecConfigurerTests {
|
||||
assertThat(this.configurer.getReaders()).isEmpty();
|
||||
assertThat(this.configurer.getWriters()).isEmpty();
|
||||
|
||||
this.configurer.customCodecs().register(new Jackson2JsonEncoder());
|
||||
this.configurer.customCodecs().register(new Jackson2JsonDecoder());
|
||||
this.configurer.customCodecs().register(new JacksonJsonEncoder());
|
||||
this.configurer.customCodecs().register(new JacksonJsonDecoder());
|
||||
this.configurer.customCodecs().register(new ServerSentEventHttpMessageReader());
|
||||
this.configurer.customCodecs().register(new ServerSentEventHttpMessageWriter());
|
||||
assertThat(this.configurer.getReaders()).hasSize(2);
|
||||
@@ -355,15 +355,15 @@ class CodecConfigurerTests {
|
||||
void cloneDefaultCodecs() {
|
||||
CodecConfigurer clone = this.configurer.clone();
|
||||
|
||||
Jackson2JsonDecoder jacksonDecoder = new Jackson2JsonDecoder();
|
||||
Jackson2JsonEncoder jacksonEncoder = new Jackson2JsonEncoder();
|
||||
JacksonJsonDecoder jacksonDecoder = new JacksonJsonDecoder();
|
||||
JacksonJsonEncoder jacksonEncoder = new JacksonJsonEncoder();
|
||||
Jaxb2XmlDecoder jaxb2Decoder = new Jaxb2XmlDecoder();
|
||||
Jaxb2XmlEncoder jaxb2Encoder = new Jaxb2XmlEncoder();
|
||||
ProtobufDecoder protoDecoder = new ProtobufDecoder();
|
||||
ProtobufEncoder protoEncoder = new ProtobufEncoder();
|
||||
|
||||
clone.defaultCodecs().jackson2JsonDecoder(jacksonDecoder);
|
||||
clone.defaultCodecs().jackson2JsonEncoder(jacksonEncoder);
|
||||
clone.defaultCodecs().jacksonJsonDecoder(jacksonDecoder);
|
||||
clone.defaultCodecs().jacksonJsonEncoder(jacksonEncoder);
|
||||
clone.defaultCodecs().jaxb2Decoder(jaxb2Decoder);
|
||||
clone.defaultCodecs().jaxb2Encoder(jaxb2Encoder);
|
||||
clone.defaultCodecs().protobufDecoder(protoDecoder);
|
||||
|
||||
@@ -54,10 +54,8 @@ import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.codec.ServerSentEventHttpMessageWriter;
|
||||
import org.springframework.http.codec.cbor.KotlinSerializationCborDecoder;
|
||||
import org.springframework.http.codec.cbor.KotlinSerializationCborEncoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.codec.json.Jackson2SmileDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2SmileEncoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
|
||||
@@ -68,6 +66,8 @@ import org.springframework.http.codec.protobuf.KotlinSerializationProtobufDecode
|
||||
import org.springframework.http.codec.protobuf.KotlinSerializationProtobufEncoder;
|
||||
import org.springframework.http.codec.protobuf.ProtobufDecoder;
|
||||
import org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter;
|
||||
import org.springframework.http.codec.smile.JacksonSmileDecoder;
|
||||
import org.springframework.http.codec.smile.JacksonSmileEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
@@ -79,6 +79,7 @@ import static org.springframework.core.ResolvableType.forClass;
|
||||
* Tests for {@link ServerCodecConfigurer}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class ServerCodecConfigurerTests {
|
||||
|
||||
@@ -104,8 +105,8 @@ class ServerCodecConfigurerTests {
|
||||
assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageReader.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2JsonDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2SmileDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonJsonDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonSmileDecoder.class);
|
||||
assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jaxb2XmlDecoder.class);
|
||||
assertStringDecoder(getNextDecoder(readers), false);
|
||||
}
|
||||
@@ -126,26 +127,26 @@ class ServerCodecConfigurerTests {
|
||||
assertThat(writers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartHttpMessageWriter.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jackson2JsonEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jackson2SmileEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonJsonEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonSmileEncoder.class);
|
||||
assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jaxb2XmlEncoder.class);
|
||||
assertSseWriter(writers);
|
||||
assertStringEncoder(getNextEncoder(writers), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jackson2EncoderOverride() {
|
||||
Jackson2JsonDecoder decoder = new Jackson2JsonDecoder();
|
||||
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
|
||||
this.configurer.defaultCodecs().jackson2JsonDecoder(decoder);
|
||||
this.configurer.defaultCodecs().jackson2JsonEncoder(encoder);
|
||||
void jacksonEncoderOverride() {
|
||||
JacksonJsonDecoder decoder = new JacksonJsonDecoder();
|
||||
JacksonJsonEncoder encoder = new JacksonJsonEncoder();
|
||||
this.configurer.defaultCodecs().jacksonJsonDecoder(decoder);
|
||||
this.configurer.defaultCodecs().jacksonJsonEncoder(encoder);
|
||||
|
||||
List<HttpMessageReader<?>> readers = this.configurer.getReaders();
|
||||
Jackson2JsonDecoder actualDecoder = findCodec(readers, Jackson2JsonDecoder.class);
|
||||
JacksonJsonDecoder actualDecoder = findCodec(readers, JacksonJsonDecoder.class);
|
||||
assertThat(actualDecoder).isSameAs(decoder);
|
||||
|
||||
List<HttpMessageWriter<?>> writers = this.configurer.getWriters();
|
||||
Jackson2JsonEncoder actualEncoder = findCodec(writers, Jackson2JsonEncoder.class);
|
||||
JacksonJsonEncoder actualEncoder = findCodec(writers, JacksonJsonEncoder.class);
|
||||
assertThat(actualEncoder).isSameAs(encoder);
|
||||
assertThat(findCodec(writers, ServerSentEventHttpMessageWriter.class).getEncoder()).isSameAs(encoder);
|
||||
}
|
||||
@@ -173,8 +174,8 @@ class ServerCodecConfigurerTests {
|
||||
|
||||
assertThat(((KotlinSerializationCborDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((KotlinSerializationProtobufDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((Jackson2JsonDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((Jackson2SmileDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((JacksonJsonDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((JacksonSmileDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((Jaxb2XmlDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((StringDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
}
|
||||
@@ -189,16 +190,16 @@ class ServerCodecConfigurerTests {
|
||||
CodecConfigurer.CustomCodecs customCodecs = this.configurer.customCodecs();
|
||||
customCodecs.register(new ByteArrayDecoder());
|
||||
customCodecs.registerWithDefaultConfig(new ByteArrayDecoder());
|
||||
customCodecs.register(new Jackson2JsonDecoder());
|
||||
customCodecs.registerWithDefaultConfig(new Jackson2JsonDecoder());
|
||||
customCodecs.register(new JacksonJsonDecoder());
|
||||
customCodecs.registerWithDefaultConfig(new JacksonJsonDecoder());
|
||||
|
||||
this.configurer.defaultCodecs().enableLoggingRequestDetails(true);
|
||||
|
||||
List<HttpMessageReader<?>> readers = this.configurer.getReaders();
|
||||
assertThat(((ByteArrayDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(256 * 1024);
|
||||
assertThat(((ByteArrayDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((Jackson2JsonDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(256 * 1024);
|
||||
assertThat(((Jackson2JsonDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
assertThat(((JacksonJsonDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(256 * 1024);
|
||||
assertThat(((JacksonJsonDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -235,7 +236,7 @@ class ServerCodecConfigurerTests {
|
||||
ServerCodecConfigurer clone = this.configurer.clone();
|
||||
|
||||
MultipartHttpMessageReader reader = new MultipartHttpMessageReader(new DefaultPartHttpMessageReader());
|
||||
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
|
||||
JacksonJsonEncoder encoder = new JacksonJsonEncoder();
|
||||
clone.defaultCodecs().multipartReader(reader);
|
||||
clone.defaultCodecs().serverSentEventEncoder(encoder);
|
||||
|
||||
@@ -319,7 +320,7 @@ class ServerCodecConfigurerTests {
|
||||
assertThat(writer.getClass()).isEqualTo(ServerSentEventHttpMessageWriter.class);
|
||||
Encoder<?> encoder = ((ServerSentEventHttpMessageWriter) writer).getEncoder();
|
||||
assertThat(encoder).isNotNull();
|
||||
assertThat(encoder.getClass()).isEqualTo(Jackson2JsonEncoder.class);
|
||||
assertThat(encoder.getClass()).isEqualTo(JacksonJsonEncoder.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user