GH-2516: Introduce JacksonUtils

Fixes: #2516

* The new `JacksonUtils` registers some well-known Jackson modules.
* Use `JacksonUtils.enhancedObjectMapper()` in the `Jackson2JsonMessageConverter`
for more straightforward configuration from target projects
This commit is contained in:
Artem Bilan
2023-12-11 17:19:26 -05:00
parent 2510338275
commit b7e77fdf1a
3 changed files with 138 additions and 4 deletions

View File

@@ -379,7 +379,13 @@ project('spring-amqp') {
optionalApi 'com.fasterxml.jackson.core:jackson-databind'
optionalApi 'com.fasterxml.jackson.core:jackson-annotations'
optionalApi 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml'
optionalApi 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'
optionalApi 'com.fasterxml.jackson.module:jackson-module-parameter-names'
optionalApi 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
optionalApi 'com.fasterxml.jackson.datatype:jackson-datatype-joda'
optionalApi ('com.fasterxml.jackson.module:jackson-module-kotlin') {
exclude group: 'org.jetbrains.kotlin'
}
// Spring Data projection message binding support
optionalApi ('org.springframework.data:spring-data-commons') {
exclude group: 'org.springframework'

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@@ -41,6 +41,7 @@ public class Jackson2JsonMessageConverter extends AbstractJackson2MessageConvert
* Construct with an internal {@link ObjectMapper} instance
* and trusted packed to all ({@code *}).
* @since 1.6.11
* @see JacksonUtils#enhancedObjectMapper()
*/
public Jackson2JsonMessageConverter() {
this("*");
@@ -53,10 +54,10 @@ public class Jackson2JsonMessageConverter extends AbstractJackson2MessageConvert
* @param trustedPackages the trusted Java packages for deserialization
* @since 1.6.11
* @see DefaultJackson2JavaTypeMapper#setTrustedPackages(String...)
* @see JacksonUtils#enhancedObjectMapper()
*/
public Jackson2JsonMessageConverter(String... trustedPackages) {
this(new ObjectMapper(), trustedPackages);
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
this(JacksonUtils.enhancedObjectMapper(), trustedPackages);
}
/**

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2023 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.amqp.support.converter;
import org.springframework.util.ClassUtils;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
/**
* The utilities for Jackson {@link ObjectMapper} instances.
*
* @author Artem Bilan
*
* @since 3.1.1
*/
public final class JacksonUtils {
private static final boolean JDK8_MODULE_PRESENT =
ClassUtils.isPresent("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", null);
private static final boolean PARAMETER_NAMES_MODULE_PRESENT =
ClassUtils.isPresent("com.fasterxml.jackson.module.paramnames.ParameterNamesModule", null);
private static final boolean JAVA_TIME_MODULE_PRESENT =
ClassUtils.isPresent("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", null);
private static final boolean JODA_MODULE_PRESENT =
ClassUtils.isPresent("com.fasterxml.jackson.datatype.joda.JodaModule", null);
private static final boolean KOTLIN_MODULE_PRESENT =
ClassUtils.isPresent("kotlin.Unit", null) &&
ClassUtils.isPresent("com.fasterxml.jackson.module.kotlin.KotlinModule", null);
/**
* Factory for {@link ObjectMapper} instances with registered well-known modules
* and disabled {@link MapperFeature#DEFAULT_VIEW_INCLUSION} and
* {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} features.
* @return the {@link ObjectMapper} instance.
*/
public static ObjectMapper enhancedObjectMapper() {
ObjectMapper objectMapper = JsonMapper.builder()
.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.build();
registerWellKnownModulesIfAvailable(objectMapper);
return objectMapper;
}
private static void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) {
if (JDK8_MODULE_PRESENT) {
objectMapper.registerModule(Jdk8ModuleProvider.MODULE);
}
if (PARAMETER_NAMES_MODULE_PRESENT) {
objectMapper.registerModule(ParameterNamesProvider.MODULE);
}
if (JAVA_TIME_MODULE_PRESENT) {
objectMapper.registerModule(JavaTimeModuleProvider.MODULE);
}
if (JODA_MODULE_PRESENT) {
objectMapper.registerModule(JodaModuleProvider.MODULE);
}
if (KOTLIN_MODULE_PRESENT) {
objectMapper.registerModule(KotlinModuleProvider.MODULE);
}
}
private JacksonUtils() {
}
private static final class Jdk8ModuleProvider {
static final com.fasterxml.jackson.databind.Module MODULE =
new com.fasterxml.jackson.datatype.jdk8.Jdk8Module();
}
private static final class ParameterNamesProvider {
static final com.fasterxml.jackson.databind.Module MODULE =
new com.fasterxml.jackson.module.paramnames.ParameterNamesModule();
}
private static final class JavaTimeModuleProvider {
static final com.fasterxml.jackson.databind.Module MODULE =
new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule();
}
private static final class JodaModuleProvider {
static final com.fasterxml.jackson.databind.Module MODULE =
new com.fasterxml.jackson.datatype.joda.JodaModule();
}
private static final class KotlinModuleProvider {
static final com.fasterxml.jackson.databind.Module MODULE =
new com.fasterxml.jackson.module.kotlin.KotlinModule.Builder().build();
}
}