Register Module beans with Jackson2ObjectMapperBuilder

Prior to this commit, Module beans were registered with all
ObjectMapper beans, but were not registered with the auto-configured
Jackson2ObjectMapperBuilder. This meant that any ObjectMapper created
with the builder but not exposed as a bean would not have the Module
beans registered with it. One such ObjectMapper is the one used by the
auto-configured MappingJackson2XmlHttpMessageConverter. This caused
XML (de)serialization to be different to JSON (de)serialization.

This commit updates JacksonAutoConfiguration to register all of the
application context's Module beans with the auto-configured
Jackson2ObjectMapperBuilder. This ensures consistent configuration
of any ObjectMapper that's created using the builder, irrespective of
whether or not that ObjectMapper is also exposed as a bean, and
also ensures that (de)serialization of JSON and XML is consistent.

See gh-2327
This commit is contained in:
Andy Wilkinson
2015-02-12 13:42:32 +00:00
parent 0899ffdadf
commit f11bcb9495
2 changed files with 54 additions and 13 deletions

View File

@@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.jackson;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;
@@ -36,6 +38,7 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.MapperFeature;
@@ -90,7 +93,8 @@ public class JacksonAutoConfigurationTests {
@Test
public void customJacksonModules() throws Exception {
this.context.register(ModulesConfig.class, JacksonAutoConfiguration.class);
this.context.register(ModuleConfig.class, MockObjectMapperConfig.class,
JacksonAutoConfiguration.class);
this.context.refresh();
ObjectMapper mapper = this.context.getBean(ObjectMapper.class);
@SuppressWarnings({ "unchecked", "unused" })
@@ -376,13 +380,19 @@ public class JacksonAutoConfigurationTests {
SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS));
}
@Configuration
protected static class ModulesConfig {
@Test
public void moduleBeansAndWellKnownModulesAreRegisteredWithTheObjectMapperBuilder() {
this.context.register(ModuleConfig.class, JacksonAutoConfiguration.class);
this.context.refresh();
ObjectMapper objectMapper = this.context.getBean(
Jackson2ObjectMapperBuilder.class).build();
assertThat(this.context.getBean(CustomModule.class).getOwners(),
hasItem((ObjectCodec) objectMapper));
assertThat(objectMapper.canSerialize(LocalDateTime.class), is(true));
}
@Bean
public Module jacksonModule() {
return new SimpleModule();
}
@Configuration
protected static class MockObjectMapperConfig {
@Bean
@Primary
@@ -392,6 +402,15 @@ public class JacksonAutoConfigurationTests {
}
@Configuration
protected static class ModuleConfig {
@Bean
public CustomModule jacksonModule() {
return new CustomModule();
}
}
@Configuration
protected static class DoubleModulesConfig {
@@ -455,4 +474,19 @@ public class JacksonAutoConfigurationTests {
this.propertyName = propertyName;
}
}
private static class CustomModule extends SimpleModule {
private Set<ObjectCodec> owners = new HashSet<ObjectCodec>();
@Override
public void setupModule(SetupContext context) {
this.owners.add(context.getOwner());
}
Set<ObjectCodec> getOwners() {
return this.owners;
}
}
}