Skip to content

Commit

Permalink
Fix Jackson builder modulesToInstall override behavior
Browse files Browse the repository at this point in the history
This commit updates Jackson2ObjectMapperBuilder in order
to ensure that modules specified via modulesToInstall
eventually override the default ones.

Closes gh-22624
  • Loading branch information
sdeleuze committed Mar 21, 2019
1 parent 7e61826 commit 0422305
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 19 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ project("spring-web") {
testCompile("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.8.11")
testCompile("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.8.11")
testCompile("com.fasterxml.jackson.module:jackson-module-kotlin:2.8.11.1")
testCompile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.11")
testCompile("com.squareup.okhttp3:mockwebserver:${okhttp3Version}")
testRuntime("com.sun.mail:javax.mail:${javamailVersion}")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
Expand Down Expand Up @@ -599,25 +599,32 @@ public <T extends ObjectMapper> T build() {
public void configure(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper must not be null");

Map<Object, Module> modulesToRegister = new LinkedHashMap<Object, Module>();
if (this.findModulesViaServiceLoader) {
// Jackson 2.2+
objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader));
for (Module module : ObjectMapper.findModules(this.moduleClassLoader)) {
modulesToRegister.put(module.getTypeId(), module);
}
}
else if (this.findWellKnownModules) {
registerWellKnownModulesIfAvailable(objectMapper);
registerWellKnownModulesIfAvailable(modulesToRegister);
}

if (this.modules != null) {
for (Module module : this.modules) {
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
objectMapper.registerModule(module);
modulesToRegister.put(module.getTypeId(), module);
}
}
if (this.moduleClasses != null) {
for (Class<? extends Module> module : this.moduleClasses) {
objectMapper.registerModule(BeanUtils.instantiate(module));
for (Class<? extends Module> moduleClass : this.moduleClasses) {
Module module = BeanUtils.instantiateClass(moduleClass);
modulesToRegister.put(module.getTypeId(), module);
}
}
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
for (Module module : modulesToRegister.values()) {
objectMapper.registerModule(module);
}

if (this.dateFormat != null) {
objectMapper.setDateFormat(this.dateFormat);
Expand Down Expand Up @@ -719,13 +726,14 @@ else if (feature instanceof MapperFeature) {
}

@SuppressWarnings("unchecked")
private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) {
private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRegister) {
// Java 7 java.nio.file.Path class present?
if (ClassUtils.isPresent("java.nio.file.Path", this.moduleClassLoader)) {
try {
Class<? extends Module> jdk7Module = (Class<? extends Module>)
Class<? extends Module> jdk7ModuleClass = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.datatype.jdk7.Jdk7Module", this.moduleClassLoader);
objectMapper.registerModule(BeanUtils.instantiateClass(jdk7Module));
Module jdk7Module = BeanUtils.instantiateClass(jdk7ModuleClass);
modulesToRegister.put(jdk7Module.getTypeId(), jdk7Module);
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jdk7 not available
Expand All @@ -735,9 +743,10 @@ private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) {
// Java 8 java.util.Optional class present?
if (ClassUtils.isPresent("java.util.Optional", this.moduleClassLoader)) {
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);
objectMapper.registerModule(BeanUtils.instantiateClass(jdk8Module));
Module jdk8Module = BeanUtils.instantiateClass(jdk8ModuleClass);
modulesToRegister.put(jdk8Module.getTypeId(), jdk8Module);
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jdk8 not available
Expand All @@ -747,9 +756,10 @@ private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) {
// Java 8 java.time package present?
if (ClassUtils.isPresent("java.time.LocalDate", this.moduleClassLoader)) {
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);
objectMapper.registerModule(BeanUtils.instantiateClass(javaTimeModule));
Module javaTimeModule = BeanUtils.instantiateClass(javaTimeModuleClass);
modulesToRegister.put(javaTimeModule.getTypeId(), javaTimeModule);
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jsr310 not available
Expand All @@ -759,9 +769,10 @@ private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) {
// Joda-Time present?
if (ClassUtils.isPresent("org.joda.time.LocalDate", this.moduleClassLoader)) {
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);
objectMapper.registerModule(BeanUtils.instantiateClass(jodaModule));
Module jodaModule = BeanUtils.instantiateClass(jodaModuleClass);
modulesToRegister.put(jodaModule.getTypeId(), jodaModule);
}
catch (ClassNotFoundException ex) {
// jackson-datatype-joda not available
Expand All @@ -771,9 +782,10 @@ private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) {
// Kotlin present?
if (ClassUtils.isPresent("kotlin.Unit", this.moduleClassLoader)) {
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);
objectMapper.registerModule(BeanUtils.instantiateClass(kotlinModule));
Module kotlinModule = BeanUtils.instantiateClass(kotlinModuleClass);
modulesToRegister.put(kotlinModule.getTypeId(), kotlinModule);
}
catch (ClassNotFoundException ex) {
// jackson-module-kotlin not available
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
Expand All @@ -21,6 +21,8 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand All @@ -38,6 +40,7 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
Expand All @@ -48,6 +51,7 @@
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
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.SerializerFactoryConfig;
import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory;
Expand All @@ -64,12 +68,14 @@
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import kotlin.ranges.IntRange;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Test;

import org.springframework.beans.FatalBeanException;
import org.springframework.util.StringUtils;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
Expand All @@ -84,6 +90,8 @@ public class Jackson2ObjectMapperBuilderTests {

private static final String DATE_FORMAT = "yyyy-MM-dd";

private static final String DATA = "{\"offsetDateTime\": \"2020-01-01T00:00:00\"}";


@Test
public void settersWithNullValues() {
Expand Down Expand Up @@ -289,6 +297,18 @@ public void customizeWellKnownModulesWithSerializer() throws JsonProcessingExcep
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) {
return ((BasicSerializerFactory) objectMapper.getSerializerFactory()).getFactoryConfig();
Expand Down Expand Up @@ -540,4 +560,50 @@ public void setList(List<T> list) {
}
}

public static class JacksonVisibilityBean {

private String property1;

public String property2;

public String getProperty3() {
return null;
}

}

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;
}

}

}

0 comments on commit 0422305

Please sign in to comment.