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:
Sébastien Deleuze
2025-05-13 12:27:02 +02:00
parent 746679f7a7
commit 5cb2f870d0
44 changed files with 3830 additions and 168 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* 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.
@@ -33,7 +33,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseCookie;
import org.springframework.http.codec.json.Jackson2CodecSupport;
import org.springframework.http.codec.JacksonCodecSupport;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
@@ -292,7 +292,7 @@ public interface EntityResponse<T> extends ServerResponse {
Builder<T> contentType(MediaType contentType);
/**
* Add a serialization hint like {@link Jackson2CodecSupport#JSON_VIEW_HINT} to
* Add a serialization hint like {@link JacksonCodecSupport#JSON_VIEW_HINT} to
* customize how the body will be serialized.
* @param key the hint key
* @param value the hint value

View File

@@ -40,7 +40,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRange;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.json.Jackson2CodecSupport;
import org.springframework.http.codec.JacksonCodecSupport;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
@@ -142,7 +142,7 @@ public interface ServerRequest {
/**
* Extract the body with the given {@code BodyExtractor} and hints.
* @param extractor the {@code BodyExtractor} that reads from the request
* @param hints the map of hints like {@link Jackson2CodecSupport#JSON_VIEW_HINT}
* @param hints the map of hints like {@link JacksonCodecSupport#JSON_VIEW_HINT}
* to use to customize body extraction
* @param <T> the type of the body returned
* @return the extracted body

View File

@@ -40,7 +40,7 @@ import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseCookie;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.json.Jackson2CodecSupport;
import org.springframework.http.codec.JacksonCodecSupport;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.web.ErrorResponse;
@@ -385,7 +385,7 @@ public interface ServerResponse {
BodyBuilder contentType(MediaType contentType);
/**
* Add a serialization hint like {@link Jackson2CodecSupport#JSON_VIEW_HINT}
* Add a serialization hint like {@link JacksonCodecSupport#JSON_VIEW_HINT}
* to customize how the body will be serialized.
* @param key the hint key
* @param value the hint value

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* 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.
@@ -121,11 +121,12 @@ public class DispatcherHandlerErrorTests {
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
assertThat(response.getBodyAsString().block()).isEqualTo("""
{"type":"about:blank",\
"title":"Not Found",\
"status":404,\
{\
"detail":"No static resource non-existing.",\
"instance":"/resources/non-existing"}\
"instance":"\\/resources\\/non-existing",\
"status":404,\
"title":"Not Found",\
"type":"about:blank"}\
""");
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* 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.
@@ -50,7 +50,7 @@ import org.springframework.http.ReactiveHttpInputMessage;
import org.springframework.http.codec.DecoderHttpMessageReader;
import org.springframework.http.codec.FormHttpMessageReader;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.JacksonJsonDecoder;
import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.FormFieldPart;
@@ -66,7 +66,7 @@ import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRe
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import static org.springframework.http.codec.json.Jackson2CodecSupport.JSON_VIEW_HINT;
import static org.springframework.http.codec.JacksonCodecSupport.JSON_VIEW_HINT;
/**
* @author Arjen Poutsma
@@ -89,7 +89,7 @@ class BodyExtractorsTests {
messageReaders.add(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
messageReaders.add(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
messageReaders.add(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
messageReaders.add(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()));
messageReaders.add(new DecoderHttpMessageReader<>(new JacksonJsonDecoder()));
messageReaders.add(new FormHttpMessageReader());
DefaultPartHttpMessageReader partReader = new DefaultPartHttpMessageReader();
messageReaders.add(partReader);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* 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.
@@ -56,7 +56,7 @@ import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.ResourceHttpMessageWriter;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.http.codec.ServerSentEventHttpMessageWriter;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.http.codec.json.JacksonJsonEncoder;
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
import org.springframework.http.server.reactive.ServerHttpRequest;
@@ -69,7 +69,7 @@ import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRe
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.codec.json.Jackson2CodecSupport.JSON_VIEW_HINT;
import static org.springframework.http.codec.JacksonCodecSupport.JSON_VIEW_HINT;
/**
* @author Arjen Poutsma
@@ -89,7 +89,7 @@ class BodyInsertersTests {
messageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));
messageWriters.add(new ResourceHttpMessageWriter());
messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
Jackson2JsonEncoder jsonEncoder = new Jackson2JsonEncoder();
JacksonJsonEncoder jsonEncoder = new JacksonJsonEncoder();
messageWriters.add(new EncoderHttpMessageWriter<>(jsonEncoder));
messageWriters.add(new ServerSentEventHttpMessageWriter(jsonEncoder));
messageWriters.add(new FormHttpMessageWriter());
@@ -140,7 +140,7 @@ class BodyInsertersTests {
StepVerifier.create(result).expectComplete().verify();
StepVerifier.create(response.getBodyAsString())
.expectNext("{\"username\":\"foo\",\"password\":\"bar\"}")
.expectNext("{\"password\":\"bar\",\"username\":\"foo\"}")
.expectComplete()
.verify();
}
@@ -169,7 +169,7 @@ class BodyInsertersTests {
Mono<Void> result = inserter.insert(response, this.context);
StepVerifier.create(result).expectComplete().verify();
StepVerifier.create(response.getBodyAsString())
.expectNext("{\"username\":\"foo\",\"password\":\"bar\"}")
.expectNext("{\"password\":\"bar\",\"username\":\"foo\"}")
.expectComplete()
.verify();
}
@@ -200,7 +200,7 @@ class BodyInsertersTests {
Mono<Void> result = inserter.insert(response, this.context);
StepVerifier.create(result).expectComplete().verify();
StepVerifier.create(response.getBodyAsString())
.expectNext("{\"username\":\"foo\",\"password\":\"bar\"}")
.expectNext("{\"password\":\"bar\",\"username\":\"foo\"}")
.expectComplete()
.verify();
}

View File

@@ -767,7 +767,7 @@ class WebClientIntegrationTests {
expectRequestCount(1);
expectRequest(request -> {
assertThat(request.getPath()).isEqualTo("/pojo/capitalize");
assertThat(request.getBody().readUtf8()).isEqualTo("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}");
assertThat(request.getBody().readUtf8()).isEqualTo("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}");
assertThat(request.getHeader(HttpHeaders.CONTENT_LENGTH)).isEqualTo("31");
assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo("application/json");
assertThat(request.getHeader(HttpHeaders.CONTENT_TYPE)).isEqualTo("application/json");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* 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.
@@ -122,11 +122,11 @@ class RequestMappingExceptionHandlingIntegrationTests extends AbstractRequestMap
.isThrownBy(() -> performGet("/no-such-handler", new HttpHeaders(), String.class))
.satisfies(ex -> {
assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(ex.getResponseBodyAsString()).isEqualTo(
"{\"type\":\"about:blank\"," +
"\"title\":\"Not Found\"," +
assertThat(ex.getResponseBodyAsString()).isEqualTo("{" +
"\"instance\":\"\\/no-such-handler\"," +
"\"status\":404," +
"\"instance\":\"/no-such-handler\"}");
"\"title\":\"Not Found\"," +
"\"type\":\"about:blank\"}");
});
}
@@ -139,11 +139,11 @@ class RequestMappingExceptionHandlingIntegrationTests extends AbstractRequestMap
.satisfies(ex -> {
assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
assertThat(ex.getResponseBodyAsString()).isEqualTo("{" +
"\"type\":\"about:blank\"," +
"\"title\":\"Bad Request\"," +
"\"status\":400," +
"\"detail\":\"Required query parameter 'q' is not present.\"," +
"\"instance\":\"/missing-request-parameter\"}");
"\"instance\":\"\\/missing-request-parameter\"," +
"\"status\":400," +
"\"title\":\"Bad Request\"," +
"\"type\":\"about:blank\"}");
});
}

View File

@@ -27,7 +27,7 @@ import reactor.test.StepVerifier;
import org.springframework.core.codec.CharSequenceEncoder;
import org.springframework.http.MediaType;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.http.codec.json.JacksonJsonEncoder;
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest;
import org.springframework.web.testfixture.server.MockServerWebExchange;
@@ -43,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
*/
class HttpMessageWriterViewTests {
private HttpMessageWriterView view = new HttpMessageWriterView(new Jackson2JsonEncoder());
private HttpMessageWriterView view = new HttpMessageWriterView(new JacksonJsonEncoder());
private final Map<String, Object> model = new HashMap<>();

View File

@@ -25,7 +25,7 @@ import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.http.codec.EncoderHttpMessageWriter
import org.springframework.http.codec.HttpMessageWriter
import org.springframework.http.codec.json.Jackson2JsonEncoder
import org.springframework.http.codec.json.JacksonJsonEncoder
import org.springframework.http.codec.json.KotlinSerializationJsonEncoder
import org.springframework.util.ObjectUtils
import org.springframework.web.bind.annotation.GetMapping
@@ -54,7 +54,7 @@ class MessageWriterResultHandlerKotlinTests {
val writerList = if (ObjectUtils.isEmpty(writers)) {
listOf(
EncoderHttpMessageWriter(KotlinSerializationJsonEncoder()),
EncoderHttpMessageWriter(Jackson2JsonEncoder())
EncoderHttpMessageWriter(JacksonJsonEncoder())
)
} else {
listOf(*writers)