提交 35010149 编写于 作者: S Sebastien Deleuze

Fix Jackson builder modulesToInstall override behavior

This commit updates Jackson2ObjectMapperBuilder in order
to ensure that modules specified via modulesToInstall
eventually override the default ones.

Closes gh-22576
上级 5f111098
...@@ -69,6 +69,7 @@ dependencies { ...@@ -69,6 +69,7 @@ dependencies {
} }
testCompile("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:${jackson2Version}") testCompile("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:${jackson2Version}")
testCompile("com.fasterxml.jackson.datatype:jackson-datatype-joda:${jackson2Version}") testCompile("com.fasterxml.jackson.datatype:jackson-datatype-joda:${jackson2Version}")
testCompile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jackson2Version}")
testCompile("com.fasterxml.jackson.module:jackson-module-kotlin:${jackson2Version}") testCompile("com.fasterxml.jackson.module:jackson-module-kotlin:${jackson2Version}")
testCompile("org.apache.tomcat:tomcat-util:${tomcatVersion}") testCompile("org.apache.tomcat:tomcat-util:${tomcatVersion}")
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
......
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -632,21 +632,24 @@ public class Jackson2ObjectMapperBuilder { ...@@ -632,21 +632,24 @@ public class Jackson2ObjectMapperBuilder {
public void configure(ObjectMapper objectMapper) { public void configure(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper must not be null"); Assert.notNull(objectMapper, "ObjectMapper must not be null");
Map<Object, Module> modulesToRegister = new LinkedHashMap<>();
if (this.findModulesViaServiceLoader) { if (this.findModulesViaServiceLoader) {
objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader)); ObjectMapper.findModules(this.moduleClassLoader).forEach(module -> modulesToRegister.put(module.getTypeId(), module));
} }
else if (this.findWellKnownModules) { else if (this.findWellKnownModules) {
registerWellKnownModulesIfAvailable(objectMapper); registerWellKnownModulesIfAvailable(modulesToRegister);
} }
if (this.modules != null) { if (this.modules != null) {
objectMapper.registerModules(this.modules); this.modules.forEach(module -> modulesToRegister.put(module.getTypeId(), module));
} }
if (this.moduleClasses != null) { if (this.moduleClasses != null) {
for (Class<? extends Module> module : this.moduleClasses) { for (Class<? extends Module> moduleClass : this.moduleClasses) {
objectMapper.registerModule(BeanUtils.instantiateClass(module)); Module module = BeanUtils.instantiateClass(moduleClass);
modulesToRegister.put(module.getTypeId(), module);
} }
} }
objectMapper.registerModules(modulesToRegister.values());
if (this.dateFormat != null) { if (this.dateFormat != null) {
objectMapper.setDateFormat(this.dateFormat); objectMapper.setDateFormat(this.dateFormat);
...@@ -744,20 +747,22 @@ public class Jackson2ObjectMapperBuilder { ...@@ -744,20 +747,22 @@ public class Jackson2ObjectMapperBuilder {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) { private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRegister) {
try { try {
Class<? extends Module> jdk8Module = (Class<? extends Module>) Class<? extends Module> jdk8ModuleClass = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", this.moduleClassLoader); ClassUtils.forName("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", this.moduleClassLoader);
objectMapper.registerModule(BeanUtils.instantiateClass(jdk8Module)); Module jdk8Module = BeanUtils.instantiateClass(jdk8ModuleClass);
modulesToRegister.put(jdk8Module.getTypeId(), jdk8Module);
} }
catch (ClassNotFoundException ex) { catch (ClassNotFoundException ex) {
// jackson-datatype-jdk8 not available // jackson-datatype-jdk8 not available
} }
try { try {
Class<? extends Module> javaTimeModule = (Class<? extends Module>) Class<? extends Module> javaTimeModuleClass = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", this.moduleClassLoader); ClassUtils.forName("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", this.moduleClassLoader);
objectMapper.registerModule(BeanUtils.instantiateClass(javaTimeModule)); Module javaTimeModule = BeanUtils.instantiateClass(javaTimeModuleClass);
modulesToRegister.put(javaTimeModule.getTypeId(), javaTimeModule);
} }
catch (ClassNotFoundException ex) { catch (ClassNotFoundException ex) {
// jackson-datatype-jsr310 not available // jackson-datatype-jsr310 not available
...@@ -766,9 +771,10 @@ public class Jackson2ObjectMapperBuilder { ...@@ -766,9 +771,10 @@ public class Jackson2ObjectMapperBuilder {
// Joda-Time present? // Joda-Time present?
if (ClassUtils.isPresent("org.joda.time.LocalDate", this.moduleClassLoader)) { if (ClassUtils.isPresent("org.joda.time.LocalDate", this.moduleClassLoader)) {
try { try {
Class<? extends Module> jodaModule = (Class<? extends Module>) Class<? extends Module> jodaModuleClass = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.datatype.joda.JodaModule", this.moduleClassLoader); ClassUtils.forName("com.fasterxml.jackson.datatype.joda.JodaModule", this.moduleClassLoader);
objectMapper.registerModule(BeanUtils.instantiateClass(jodaModule)); Module jodaModule = BeanUtils.instantiateClass(jodaModuleClass);
modulesToRegister.put(jodaModule.getTypeId(), jodaModule);
} }
catch (ClassNotFoundException ex) { catch (ClassNotFoundException ex) {
// jackson-datatype-joda not available // jackson-datatype-joda not available
...@@ -778,9 +784,10 @@ public class Jackson2ObjectMapperBuilder { ...@@ -778,9 +784,10 @@ public class Jackson2ObjectMapperBuilder {
// Kotlin present? // Kotlin present?
if (KotlinDetector.isKotlinPresent()) { if (KotlinDetector.isKotlinPresent()) {
try { try {
Class<? extends Module> kotlinModule = (Class<? extends Module>) Class<? extends Module> kotlinModuleClass = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", this.moduleClassLoader); ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", this.moduleClassLoader);
objectMapper.registerModule(BeanUtils.instantiateClass(kotlinModule)); Module kotlinModule = BeanUtils.instantiateClass(kotlinModuleClass);
modulesToRegister.put(kotlinModule.getTypeId(), kotlinModule);
} }
catch (ClassNotFoundException ex) { catch (ClassNotFoundException ex) {
if (!kotlinWarningLogged) { if (!kotlinWarningLogged) {
......
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -21,6 +21,8 @@ import java.io.UnsupportedEncodingException; ...@@ -21,6 +21,8 @@ import java.io.UnsupportedEncodingException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.time.format.DateTimeParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
...@@ -40,6 +42,7 @@ import com.fasterxml.jackson.core.JsonGenerator; ...@@ -40,6 +42,7 @@ import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonMappingException;
...@@ -50,6 +53,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -50,6 +53,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig; import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig; import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory; import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory;
...@@ -68,12 +72,14 @@ import com.fasterxml.jackson.databind.type.SimpleType; ...@@ -68,12 +72,14 @@ import com.fasterxml.jackson.databind.type.SimpleType;
import com.fasterxml.jackson.dataformat.cbor.CBORFactory; import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
import com.fasterxml.jackson.dataformat.smile.SmileFactory; import com.fasterxml.jackson.dataformat.smile.SmileFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import kotlin.ranges.IntRange; import kotlin.ranges.IntRange;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.FatalBeanException; import org.springframework.beans.FatalBeanException;
import org.springframework.util.StringUtils;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
...@@ -96,6 +102,8 @@ public class Jackson2ObjectMapperBuilderTests { ...@@ -96,6 +102,8 @@ public class Jackson2ObjectMapperBuilderTests {
private static final String DATE_FORMAT = "yyyy-MM-dd"; private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATA = "{\"offsetDateTime\": \"2020-01-01T00:00:00\"}";
@Test(expected = FatalBeanException.class) @Test(expected = FatalBeanException.class)
public void unknownFeature() { public void unknownFeature() {
...@@ -308,6 +316,18 @@ public class Jackson2ObjectMapperBuilderTests { ...@@ -308,6 +316,18 @@ public class Jackson2ObjectMapperBuilderTests {
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid")); assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
} }
@Test // gh-22576
public void overrideWellKnownModuleWithModule() throws IOException {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(OffsetDateTime.class, new OffsetDateTimeDeserializer());
builder.modulesToInstall(javaTimeModule);
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
ObjectMapper objectMapper = builder.build();
DemoPojo demoPojo = objectMapper.readValue(DATA, DemoPojo.class);
assertNotNull(demoPojo.getOffsetDateTime());
}
private static SerializerFactoryConfig getSerializerFactoryConfig(ObjectMapper objectMapper) { private static SerializerFactoryConfig getSerializerFactoryConfig(ObjectMapper objectMapper) {
return ((BasicSerializerFactory) objectMapper.getSerializerFactory()).getFactoryConfig(); return ((BasicSerializerFactory) objectMapper.getSerializerFactory()).getFactoryConfig();
...@@ -613,4 +633,38 @@ public class Jackson2ObjectMapperBuilderTests { ...@@ -613,4 +633,38 @@ public class Jackson2ObjectMapperBuilderTests {
} }
static class OffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> {
private static final String CURRENT_ZONE_OFFSET = OffsetDateTime.now().getOffset().toString();
@Override
public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
final String value = jsonParser.getValueAsString();
if (StringUtils.isEmpty(value)) {
return null;
}
try {
return OffsetDateTime.parse(value);
} catch (DateTimeParseException exception) {
return OffsetDateTime.parse(value + CURRENT_ZONE_OFFSET);
}
}
}
@JsonDeserialize
static class DemoPojo {
private OffsetDateTime offsetDateTime;
public OffsetDateTime getOffsetDateTime() {
return offsetDateTime;
}
public void setOffsetDateTime(OffsetDateTime offsetDateTime) {
this.offsetDateTime = offsetDateTime;
}
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册