Polish Jackson 3 support

- Improve Javadoc.

- Suppress warnings for "removal".

- Update copyright headers.

- Migrate several tests from:
  - MappingJackson2MessageConverter to JacksonJsonMessageConverter
  - Jackson2JsonEncoder to JacksonJsonEncoder
  - Jackson2JsonDecoder to JacksonJsonDecoder
  - Jackson2SmileEncoder to JacksonSmileEncoder
  - Jackson2ObjectMapperBuilder to JsonMapper and XmlMapper
  - MappingJackson2JsonView to JacksonJsonView
  - MappingJackson2HttpMessageConverter to JacksonJsonHttpMessageConverter
  - MappingJackson2XmlHttpMessageConverter to JacksonXmlHttpMessageConverter
This commit is contained in:
Sam Brannen
2025-05-14 16:55:27 +02:00
parent ea340fbe69
commit 01fea5e7ed
88 changed files with 402 additions and 346 deletions

View File

@@ -30,6 +30,7 @@ import org.springframework.http.server.ServerHttpResponse;
/**
* A convenient base class for {@code ResponseBodyAdvice} implementations
* that customize the response before JSON serialization with
* {@link AbstractJacksonHttpMessageConverter}'s and
* {@link AbstractJackson2HttpMessageConverter}'s concrete subclasses.
*
* @author Rossen Stoyanchev

View File

@@ -71,6 +71,9 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
*/
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
protected enum ConverterType { BASE, GENERIC, SMART };
private static final Set<HttpMethod> SUPPORTED_METHODS = Set.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
private static final Object NO_VALUE = new Object();
@@ -80,8 +83,6 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
protected final List<HttpMessageConverter<?>> messageConverters;
protected enum ConverterType { BASE, GENERIC, SMART };
private final RequestResponseBodyAdviceChain advice;

View File

@@ -19,7 +19,6 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -93,29 +92,29 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
private static final List<MediaType> ALL_APPLICATION_MEDIA_TYPES =
List.of(MediaType.ALL, new MediaType("application"));
private static final List<MediaType> PROBLEM_MEDIA_TYPES =
List.of(MediaType.APPLICATION_PROBLEM_JSON, MediaType.APPLICATION_PROBLEM_XML);
private static final Type RESOURCE_REGION_LIST_TYPE =
new ParameterizedTypeReference<List<ResourceRegion>>() {}.getType();
private final ContentNegotiationManager contentNegotiationManager;
private final List<MediaType> problemMediaTypes =
Arrays.asList(MediaType.APPLICATION_PROBLEM_JSON, MediaType.APPLICATION_PROBLEM_XML);
private final List<ErrorResponse.Interceptor> errorResponseInterceptors = new ArrayList<>();
private final Set<String> safeExtensions = new HashSet<>();
/**
* Constructor with list of converters only.
* Construct with the provided list of converters only.
*/
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters) {
this(converters, null, null);
}
/**
* Constructor with list of converters and ContentNegotiationManager.
* Construct with the provided list of converters and {@link ContentNegotiationManager}.
*/
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable ContentNegotiationManager contentNegotiationManager) {
@@ -124,9 +123,8 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
}
/**
* Variant of {@link #AbstractMessageConverterMethodProcessor(List)}
* with an additional {@link ContentNegotiationManager} for return
* value handling.
* Variant of {@link #AbstractMessageConverterMethodProcessor(List, ContentNegotiationManager)}
* with an additional {@code requestResponseBodyAdvice} list for return value handling.
*/
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable ContentNegotiationManager manager, @Nullable List<Object> requestResponseBodyAdvice) {
@@ -136,8 +134,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
/**
* Variant of {@link #AbstractMessageConverterMethodProcessor(List, ContentNegotiationManager, List)}
* with additional list of {@link ErrorResponse.Interceptor}s for return
* value handling.
* with additional list of {@link ErrorResponse.Interceptor}s for return value handling.
* @since 6.2
*/
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters,
@@ -154,7 +151,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
/**
* Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}.
* Create a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}.
* @param webRequest the web request to create an output message from
* @return the output message
*/
@@ -192,10 +189,10 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
}
/**
* Writes the given return type to the given output message.
* Write the given return type to the given output message.
* @param value the value to write to the output message
* @param returnType the type of the value
* @param inputMessage the input messages. Used to inspect the {@code Accept} header.
* @param inputMessage the input messages, used to inspect the {@code Accept} header
* @param outputMessage the output message to write to
* @throws IOException thrown in case of I/O errors
* @throws HttpMediaTypeNotAcceptableException thrown when the conditions indicated
@@ -280,7 +277,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
// For ProblemDetail, fall back on RFC 9457 format
if (compatibleMediaTypes.isEmpty() && ProblemDetail.class.isAssignableFrom(valueType)) {
determineCompatibleMediaTypes(this.problemMediaTypes, producibleTypes, compatibleMediaTypes);
determineCompatibleMediaTypes(PROBLEM_MEDIA_TYPES, producibleTypes, compatibleMediaTypes);
}
if (compatibleMediaTypes.isEmpty()) {

View File

@@ -90,4 +90,5 @@ public class JsonViewRequestBodyAdvice extends RequestBodyAdviceAdapter {
}
return classes[0];
}
}

View File

@@ -79,4 +79,5 @@ public class JsonViewResponseBodyAdvice extends AbstractMappingJacksonResponseBo
}
return classes[0];
}
}

View File

@@ -213,4 +213,5 @@ class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyA
}
return hints;
}
}

View File

@@ -61,7 +61,9 @@ public abstract class AbstractJacksonView extends AbstractView {
protected static final String FILTER_PROVIDER_HINT = FilterProvider.class.getName();
private static volatile @Nullable List<JacksonModule> modules = null;
private static volatile @Nullable List<JacksonModule> modules;
private final ObjectMapper objectMapper;
@@ -84,6 +86,7 @@ public abstract class AbstractJacksonView extends AbstractView {
setExposePathVariables(false);
}
private List<JacksonModule> initModules() {
if (modules == null) {
modules = MapperBuilder.findModules(AbstractJacksonHttpMessageConverter.class.getClassLoader());
@@ -94,7 +97,7 @@ public abstract class AbstractJacksonView extends AbstractView {
/**
* Set the {@code JsonEncoding} for this view.
* By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used.
* <p>Default is {@linkplain JsonEncoding#UTF8 UTF-8}.
*/
public void setEncoding(JsonEncoding encoding) {
Assert.notNull(encoding, "'encoding' must not be null");
@@ -110,7 +113,8 @@ public abstract class AbstractJacksonView extends AbstractView {
/**
* Disables caching of the generated JSON.
* <p>Default is {@code true}, which will prevent the client from caching the generated JSON.
* <p>Default is {@code true}, which will prevent the client from caching the
* generated JSON.
*/
public void setDisableCaching(boolean disableCaching) {
this.disableCaching = disableCaching;
@@ -206,13 +210,13 @@ public abstract class AbstractJacksonView extends AbstractView {
/**
* Set the attribute in the model that should be rendered by this view.
* When set, all other model attributes will be ignored.
* <p>When set, all other model attributes will be ignored.
*/
public abstract void setModelKey(String modelKey);
/**
* Filter out undesired attributes from the given model.
* The return value can be either another {@link Map} or a single value object.
* <p>The return value can be either another {@link Map} or a single value object.
* @param model the model, as passed on to {@link #renderMergedOutputModel}
* @param request current HTTP request
* @return the value to be rendered
@@ -221,16 +225,16 @@ public abstract class AbstractJacksonView extends AbstractView {
/**
* Write a prefix before the main content.
* @param generator the generator to use for writing content.
* @param object the object to write to the output message.
* @param generator the generator to use for writing content
* @param object the object to write to the output message
*/
protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
}
/**
* Write a suffix after the main content.
* @param generator the generator to use for writing content.
* @param object the object to write to the output message.
* @param generator the generator to use for writing content
* @param object the object to write to the output message
*/
protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {
}

View File

@@ -55,11 +55,12 @@ import org.springframework.web.servlet.view.AbstractJacksonView;
public class JacksonJsonView extends AbstractJacksonView {
/**
* Default content type: "application/json".
* Overridable through {@link #setContentType}.
* Default content type: {@value}.
* <p>Overridable through {@link #setContentType(String)}.
*/
public static final String DEFAULT_CONTENT_TYPE = "application/json";
private @Nullable String jsonPrefix;
private @Nullable Set<String> modelKeys;
@@ -79,7 +80,7 @@ public class JacksonJsonView extends AbstractJacksonView {
/**
* Construct a new instance using the provided {@link ObjectMapper}
* and setting the content type to {@code application/json}.
* and setting the content type to {@value #DEFAULT_CONTENT_TYPE}.
*/
public JacksonJsonView(ObjectMapper objectMapper) {
super(objectMapper, DEFAULT_CONTENT_TYPE);
@@ -88,7 +89,7 @@ public class JacksonJsonView extends AbstractJacksonView {
/**
* Specify a custom prefix to use for this view's JSON output.
* Default is none.
* <p>Default is none.
* @see #setPrefixJson
*/
public void setJsonPrefix(String jsonPrefix) {
@@ -97,7 +98,7 @@ public class JacksonJsonView extends AbstractJacksonView {
/**
* Indicates whether the JSON output by this view should be prefixed with <code>")]}', "</code>.
* Default is {@code false}.
* <p>Default is {@code false}.
* <p>Prefixing the JSON string in this manner is used to help prevent JSON Hijacking.
* The prefix renders the string syntactically invalid as a script so that it cannot be hijacked.
* This prefix should be stripped before parsing the string as JSON.
@@ -114,7 +115,7 @@ public class JacksonJsonView extends AbstractJacksonView {
/**
* Set the attributes in the model that should be rendered by this view.
* When set, all other model attributes will be ignored.
* <p>When set, all other model attributes will be ignored.
*/
public void setModelKeys(@Nullable Set<String> modelKeys) {
this.modelKeys = modelKeys;
@@ -141,7 +142,7 @@ public class JacksonJsonView extends AbstractJacksonView {
/**
* Filter out undesired attributes from the given model.
* The return value can be either another {@link Map} or a single value object.
* <p>The return value can be either another {@link Map} or a single value object.
* <p>The default implementation removes {@link BindingResult} instances and entries
* not included in the {@link #setModelKeys modelKeys} property.
* @param model the model, as passed on to {@link #renderMergedOutputModel}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.web.servlet.view.AbstractJacksonView;
* using <a href="https://github.com/FasterXML/jackson">Jackson 3's</a> {@link XmlMapper}.
*
* <p>The Object to be serialized is supplied as a parameter in the model. The first serializable
* entry is used. Users can either specify a specific entry in the model via the
* entry is used. Users can specify a specific entry in the model via the
* {@link #setModelKey(String) sourceKey} property.
*
* <p>The following special model entries are supported:
@@ -51,7 +51,8 @@ import org.springframework.web.servlet.view.AbstractJacksonView;
public class JacksonXmlView extends AbstractJacksonView {
/**
* The default content type for the view.
* Default content type: {@value}.
* <p>Overridable through {@link #setContentType(String)}.
*/
public static final String DEFAULT_CONTENT_TYPE = "application/xml";
@@ -60,7 +61,7 @@ public class JacksonXmlView extends AbstractJacksonView {
/**
* Construct a new instance with a {@link XmlMapper} customized with
* Construct a new instance with an {@link XmlMapper} customized with
* the {@link tools.jackson.databind.JacksonModule}s found by
* {@link MapperBuilder#findModules(ClassLoader)} and setting
* the content type to {@code application/xml}.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.

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.
@@ -171,6 +171,7 @@ class ViewResolverRegistryTests {
}
@Test
@SuppressWarnings("removal")
void contentNegotiation() {
MappingJackson2JsonView view = new MappingJackson2JsonView();
this.registry.enableContentNegotiation(view);
@@ -180,6 +181,7 @@ class ViewResolverRegistryTests {
}
@Test
@SuppressWarnings("removal")
void contentNegotiationAddsDefaultViewRegistrations() {
MappingJackson2JsonView view1 = new MappingJackson2JsonView();
this.registry.enableContentNegotiation(view1);

View File

@@ -22,10 +22,12 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.MapperFeature;
import tools.jackson.databind.ObjectMapper;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.testfixture.beans.TestBean;
@@ -35,7 +37,7 @@ import org.springframework.format.FormatterRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
@@ -79,13 +81,11 @@ import org.springframework.web.servlet.resource.ResourceUrlProviderExposingInter
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.ViewResolverComposite;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.springframework.web.servlet.view.json.JacksonJsonView;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockServletContext;
import org.springframework.web.util.UrlPathHelper;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static com.fasterxml.jackson.databind.MapperFeature.DEFAULT_VIEW_INCLUSION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.springframework.http.MediaType.APPLICATION_JSON;
@@ -211,11 +211,11 @@ class WebMvcConfigurationSupportExtensionTests {
List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
assertThat(converters).hasSize(2);
assertThat(converters.get(0).getClass()).isEqualTo(StringHttpMessageConverter.class);
assertThat(converters.get(1).getClass()).isEqualTo(MappingJackson2HttpMessageConverter.class);
ObjectMapper objectMapper = ((MappingJackson2HttpMessageConverter) converters.get(1)).getObjectMapper();
assertThat(objectMapper.getDeserializationConfig().isEnabled(DEFAULT_VIEW_INCLUSION)).isFalse();
assertThat(objectMapper.getSerializationConfig().isEnabled(DEFAULT_VIEW_INCLUSION)).isFalse();
assertThat(objectMapper.getDeserializationConfig().isEnabled(FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
assertThat(converters.get(1).getClass()).isEqualTo(JacksonJsonHttpMessageConverter.class);
ObjectMapper objectMapper = ((JacksonJsonHttpMessageConverter) converters.get(1)).getObjectMapper();
assertThat(objectMapper.deserializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)).isFalse();
assertThat(objectMapper.deserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
assertThat(objectMapper.serializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)).isFalse();
DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(adapter);
@@ -313,7 +313,7 @@ class WebMvcConfigurationSupportExtensionTests {
List<View> defaultViews = (List<View>) accessor.getPropertyValue("defaultViews");
assertThat(defaultViews).isNotNull();
assertThat(defaultViews).hasSize(1);
assertThat(defaultViews.get(0).getClass()).isEqualTo(MappingJackson2JsonView.class);
assertThat(defaultViews.get(0).getClass()).isEqualTo(JacksonJsonView.class);
viewResolvers = (List<ViewResolver>) accessor.getPropertyValue("viewResolvers");
assertThat(viewResolvers).isNotNull();
@@ -356,7 +356,7 @@ class WebMvcConfigurationSupportExtensionTests {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new JacksonJsonHttpMessageConverter());
}
@Override
@@ -449,7 +449,7 @@ class WebMvcConfigurationSupportExtensionTests {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation(new MappingJackson2JsonView());
registry.enableContentNegotiation(new JacksonJsonView());
registry.jsp("/", ".jsp");
}

View File

@@ -21,10 +21,12 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.MapperFeature;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.dataformat.xml.XmlMapper;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.ApplicationContext;
@@ -43,9 +45,9 @@ import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.http.converter.xml.JacksonXmlHttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
@@ -93,8 +95,6 @@ import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import org.springframework.web.testfixture.servlet.MockServletContext;
import org.springframework.web.util.UrlPathHelper;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static com.fasterxml.jackson.databind.MapperFeature.DEFAULT_VIEW_INCLUSION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.web.servlet.DispatcherServlet.FLASH_MAP_MANAGER_BEAN_NAME;
import static org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME;
@@ -174,14 +174,16 @@ class WebMvcConfigurationSupportTests {
List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
assertThat(converters).hasSizeGreaterThanOrEqualTo(13);
converters.stream()
.filter(AbstractJackson2HttpMessageConverter.class::isInstance)
.filter(AbstractJacksonHttpMessageConverter.class::isInstance)
.map(AbstractJacksonHttpMessageConverter.class::cast)
.forEach(converter -> {
ObjectMapper mapper = ((AbstractJackson2HttpMessageConverter) converter).getObjectMapper();
assertThat(mapper.getDeserializationConfig().isEnabled(DEFAULT_VIEW_INCLUSION)).isFalse();
assertThat(mapper.getSerializationConfig().isEnabled(DEFAULT_VIEW_INCLUSION)).isFalse();
assertThat(mapper.getDeserializationConfig().isEnabled(FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
if (converter instanceof MappingJackson2XmlHttpMessageConverter) {
assertThat(mapper.getClass()).isEqualTo(XmlMapper.class);
ObjectMapper mapper = converter.getObjectMapper();
assertThat(mapper.deserializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)).isFalse();
assertThat(mapper.deserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse();
assertThat(mapper.serializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)).isFalse();
if (converter instanceof JacksonXmlHttpMessageConverter) {
assertThat(mapper).isExactlyInstanceOf(XmlMapper.class);
}
});

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.
@@ -48,7 +48,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.BindException;
@@ -65,6 +65,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
/**
* Tests for {@link DefaultServerRequest}.
*
* @author Arjen Poutsma
*/
class DefaultServerRequestTests {
@@ -305,7 +306,7 @@ class DefaultServerRequestTests {
servletRequest.setContent("[\"foo\",\"bar\"]".getBytes(UTF_8));
DefaultServerRequest request = new DefaultServerRequest(servletRequest,
List.of(new MappingJackson2HttpMessageConverter()));
List.of(new JacksonJsonHttpMessageConverter()));
List<String> result = request.body(new ParameterizedTypeReference<>() {});
assertThat(result).containsExactly("foo", "bar");

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.
@@ -38,7 +38,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.servlet.ModelAndView;
@@ -294,7 +294,7 @@ class DefaultServerResponseBuilderTests {
MockHttpServletRequest mockRequest = new MockHttpServletRequest("GET", "https://example.com");
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
ServerResponse.Context context = () -> Collections.singletonList(new MappingJackson2HttpMessageConverter());
ServerResponse.Context context = () -> Collections.singletonList(new JacksonJsonHttpMessageConverter());
ModelAndView mav = response.writeTo(mockRequest, mockResponse, context);
assertThat(mav).isNull();

View File

@@ -20,11 +20,15 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.SerializationFeature;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
@@ -33,23 +37,24 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ServerResponse.SseBuilder}.
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @author Brian Clozel
*/
class SseServerResponseTests {
private MockHttpServletRequest mockRequest;
private final MockHttpServletRequest mockRequest = new MockHttpServletRequest("GET", "https://example.com");
private final MockHttpServletResponse mockResponse = new MockHttpServletResponse();
private MockHttpServletResponse mockResponse;
@BeforeEach
void setUp() {
this.mockRequest = new MockHttpServletRequest("GET", "https://example.com");
this.mockRequest.setAsyncSupported(true);
this.mockResponse = new MockHttpServletResponse();
}
@Test
void sendString() throws Exception {
String body = "foo bar";
@@ -83,7 +88,7 @@ class SseServerResponseTests {
}
});
ServerResponse.Context context = () -> Collections.singletonList(new MappingJackson2HttpMessageConverter());
ServerResponse.Context context = () -> List.of(new JacksonJsonHttpMessageConverter());
ModelAndView mav = response.writeTo(this.mockRequest, this.mockResponse, context);
assertThat(mav).isNull();
@@ -104,9 +109,9 @@ class SseServerResponseTests {
}
});
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setPrettyPrint(true);
ServerResponse.Context context = () -> Collections.singletonList(converter);
ObjectMapper objectMapper = JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build();
JacksonJsonHttpMessageConverter converter = new JacksonJsonHttpMessageConverter(objectMapper);
ServerResponse.Context context = () -> List.of(converter);
ModelAndView mav = response.writeTo(this.mockRequest, this.mockResponse, context);
assertThat(mav).isNull();

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.
@@ -20,13 +20,14 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
@@ -35,21 +36,22 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link StreamingServerResponse}.
*
* @author Brian Clozel
*/
class StreamingServerResponseTests {
private MockHttpServletRequest mockRequest;
private final MockHttpServletRequest mockRequest = new MockHttpServletRequest("GET", "https://example.com");
private final MockHttpServletResponse mockResponse = new MockHttpServletResponse();
private MockHttpServletResponse mockResponse;
@BeforeEach
void setUp() {
this.mockRequest = new MockHttpServletRequest("GET", "https://example.com");
this.mockRequest.setAsyncSupported(true);
this.mockResponse = new MockHttpServletResponse();
}
@Test
void writeSingleString() throws Exception {
String body = "data: foo bar\n\n";
@@ -113,7 +115,7 @@ class StreamingServerResponseTests {
}
});
ServerResponse.Context context = () -> Collections.singletonList(new MappingJackson2HttpMessageConverter());
ServerResponse.Context context = () -> List.of(new JacksonJsonHttpMessageConverter());
ModelAndView mav = response.writeTo(this.mockRequest, this.mockResponse, context);
assertThat(mav).isNull();
assertThat(this.mockResponse.getContentType()).isEqualTo(MediaType.APPLICATION_NDJSON.toString());
@@ -125,7 +127,6 @@ class StreamingServerResponseTests {
record Person(String name, int age) {
}
}

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.
@@ -30,6 +30,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter;
@@ -40,7 +41,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -104,7 +105,7 @@ public class HttpEntityMethodProcessorTests {
this.servletRequest.setContentType("application/json");
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new JacksonJsonHttpMessageConverter());
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
@SuppressWarnings("unchecked")
@@ -121,7 +122,7 @@ public class HttpEntityMethodProcessorTests {
this.servletRequest.setContentType("application/json");
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new JacksonJsonHttpMessageConverter());
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
HttpEntity<?> result = (HttpEntity<?>) processor.resolveArgument(this.paramSimpleBean,
@@ -138,7 +139,7 @@ public class HttpEntityMethodProcessorTests {
this.servletRequest.setContentType("application/json");
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new JacksonJsonHttpMessageConverter());
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
@SuppressWarnings("unchecked")
@@ -151,6 +152,7 @@ public class HttpEntityMethodProcessorTests {
}
@Test
@Disabled("Determine why this fails with JacksonJsonHttpMessageConverter but passes with MappingJackson2HttpMessageConverter")
void resolveArgumentTypeVariable() throws Exception {
Method method = MySimpleParameterizedController.class.getMethod("handleDto", HttpEntity.class);
HandlerMethod handlerMethod = new HandlerMethod(new MySimpleParameterizedController(), method);
@@ -161,7 +163,7 @@ public class HttpEntityMethodProcessorTests {
this.servletRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new JacksonJsonHttpMessageConverter());
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
@SuppressWarnings("unchecked")
@@ -179,7 +181,7 @@ public class HttpEntityMethodProcessorTests {
MethodParameter methodReturnType = handlerMethod.getReturnType();
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new JacksonJsonHttpMessageConverter());
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
Object returnValue = new JacksonController().handleList();

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.
@@ -263,6 +263,8 @@ class RequestMappingHandlerAdapterTests {
}
@Test // gh-15486
@SuppressWarnings("removal")
// TODO Migrate from MappingJackson2HttpMessageConverter and MappingJacksonValue to JacksonJsonHttpMessageConverter.
public void responseBodyAdvice() throws Exception {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
@@ -424,6 +426,7 @@ class RequestMappingHandlerAdapterTests {
extends AbstractMappingJacksonResponseBodyAdvice implements RequestBodyAdvice {
@Override
@SuppressWarnings("removal")
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {

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.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileSystemUtils;
@@ -133,7 +133,7 @@ class RequestPartIntegrationTests {
converters.add(emptyBodyConverter);
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new ResourceHttpMessageConverter());
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new JacksonJsonHttpMessageConverter());
AllEncompassingFormHttpMessageConverter converter = new AllEncompassingFormHttpMessageConverter();
converter.setPartConverters(converters);

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.

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.
@@ -95,7 +95,7 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
@@ -1026,7 +1026,7 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
initDispatcherServlet(ResponseEntityController.class, usePathPatterns, wac -> {
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new MappingJackson2HttpMessageConverter());
messageConverters.add(new JacksonJsonHttpMessageConverter());
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
adapterDef.getPropertyValues().add("messageConverters", messageConverters);
wac.registerBeanDefinition("handlerAdapter", adapterDef);
@@ -1204,7 +1204,7 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
void produces(boolean usePathPatterns) throws Exception {
initDispatcherServlet(ProducesController.class, usePathPatterns, wac -> {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new JacksonJsonHttpMessageConverter());
converters.add(new Jaxb2RootElementHttpMessageConverter());
RootBeanDefinition beanDef;

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.
@@ -32,7 +32,7 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
@@ -140,11 +140,13 @@ class ResourceHttpRequestHandlerIntegrationTests {
assertThat(response.getStatus()).isEqualTo(404);
assertThat(response.getContentType()).isEqualTo("application/problem+json");
assertThat(response.getContentAsString()).isEqualTo("""
{"type":"about:blank",\
"title":"Not Found",\
"status":404,\
{\
"detail":"No static resource non-existing.",\
"instance":"/cp/non-existing"}\
"instance":"\\/cp\\/non-existing",\
"status":404,\
"title":"Not Found",\
"type":"about:blank"\
}\
""");
}
@@ -212,8 +214,9 @@ class ResourceHttpRequestHandlerIntegrationTests {
}
@Override
@SuppressWarnings("removal")
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new JacksonJsonHttpMessageConverter());
}
}

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.
@@ -91,7 +91,7 @@ class JacksonJsonViewTests {
assertThat(response.getHeader("Cache-Control")).isEqualTo("no-store");
MediaType mediaType = MediaType.parseMediaType(response.getContentType());
assertThat(mediaType.isCompatibleWith(MediaType.parseMediaType(MappingJackson2JsonView.DEFAULT_CONTENT_TYPE))).isTrue();
assertThat(mediaType.isCompatibleWith(MediaType.parseMediaType(JacksonJsonView.DEFAULT_CONTENT_TYPE))).isTrue();
String jsonResult = response.getContentAsString();
assertThat(jsonResult).isNotEmpty();

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.
@@ -61,6 +61,7 @@ import static org.mockito.Mockito.mock;
* @author Sebastien Deleuze
* @author Sam Brannen
*/
@SuppressWarnings("removal")
class MappingJackson2JsonViewTests {
private MappingJackson2JsonView view = new MappingJackson2JsonView();

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.
@@ -83,7 +83,7 @@ class JacksonXmlViewTests {
assertThat(response.getHeader("Cache-Control")).isEqualTo("no-store");
MediaType mediaType = MediaType.parseMediaType(response.getContentType());
assertThat(mediaType.isCompatibleWith(MediaType.parseMediaType(MappingJackson2XmlView.DEFAULT_CONTENT_TYPE))).isTrue();
assertThat(mediaType.isCompatibleWith(MediaType.parseMediaType(JacksonXmlView.DEFAULT_CONTENT_TYPE))).isTrue();
String jsonResult = response.getContentAsString();
assertThat(jsonResult).isNotEmpty();

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.
@@ -52,6 +52,7 @@ import static org.mockito.Mockito.mock;
* @author Sebastien Deleuze
* @author Sam Brannen
*/
@SuppressWarnings("removal")
class MappingJackson2XmlViewTests {
private MappingJackson2XmlView view = new MappingJackson2XmlView();