Combined backport of Jackson configuration enhancements: Locale/TimeZone settings, refined module configuration

Issue: SPR-12594
Issue: SPR-12634
This commit is contained in:
Juergen Hoeller 2015-02-11 19:03:54 +01:00
parent 1646f62700
commit fab8dd3d0b
5 changed files with 404 additions and 40 deletions

View File

@ -697,6 +697,7 @@ project("spring-web") {
testCompile("org.apache.taglibs:taglibs-standard-jstlel:1.2.1") {
exclude group: "org.apache.taglibs", module: "taglibs-standard-spec"
}
testCompile("com.fasterxml.jackson.datatype:jackson-datatype-joda:${jackson2Version}")
testRuntime("com.sun.mail:javax.mail:1.5.2")
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -18,11 +18,14 @@ package org.springframework.http.converter.json;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
@ -46,6 +49,7 @@ import org.springframework.beans.FatalBeanException;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* A builder used to create {@link ObjectMapper} instances with a fluent API.
@ -75,6 +79,10 @@ public class Jackson2ObjectMapperBuilder {
private DateFormat dateFormat;
private Locale locale;
private TimeZone timeZone;
private AnnotationIntrospector annotationIntrospector;
private PropertyNamingStrategy propertyNamingStrategy;
@ -91,9 +99,11 @@ public class Jackson2ObjectMapperBuilder {
private List<Module> modules;
private Class<? extends Module>[] modulesToInstall;
private Class<? extends Module>[] moduleClasses;
private boolean findModulesViaServiceLoader;
private boolean findModulesViaServiceLoader = false;
private boolean findWellKnownModules = true;
private ClassLoader moduleClassLoader = getClass().getClassLoader();
@ -134,6 +144,48 @@ public class Jackson2ObjectMapperBuilder {
return this;
}
/**
* Override the default {@link Locale} to use for formatting.
* Default value used is {@link Locale#getDefault()}.
* @since 4.1.5
*/
public Jackson2ObjectMapperBuilder locale(Locale locale) {
this.locale = locale;
return this;
}
/**
* Override the default {@link Locale} to use for formatting.
* Default value used is {@link Locale#getDefault()}.
* @param localeString the locale ID as a String representation
* @since 4.1.5
*/
public Jackson2ObjectMapperBuilder locale(String localeString) {
this.locale = StringUtils.parseLocaleString(localeString);
return this;
}
/**
* Override the default {@link TimeZone} to use for formatting.
* Default value used is UTC (NOT local timezone).
* @since 4.1.5
*/
public Jackson2ObjectMapperBuilder timeZone(TimeZone timeZone) {
this.timeZone = timeZone;
return this;
}
/**
* Override the default {@link TimeZone} to use for formatting.
* Default value used is UTC (NOT local timezone).
* @param timeZoneString the zone ID as a String representation
* @since 4.1.5
*/
public Jackson2ObjectMapperBuilder timeZone(String timeZoneString) {
this.timeZone = StringUtils.parseTimeZoneString(timeZoneString);
return this;
}
/**
* Set an {@link AnnotationIntrospector} for both serialization and deserialization.
*/
@ -337,6 +389,21 @@ public class Jackson2ObjectMapperBuilder {
return this;
}
/**
* Specify one or more modules to be registered with the {@link ObjectMapper}.
* <p>Note: If this is set, no finding of modules is going to happen - not by
* Jackson, and not by Spring either (see {@link #findModulesViaServiceLoader}).
* As a consequence, specifying an empty list here will suppress any kind of
* module detection.
* <p>Specify either this or {@link #modulesToInstall}, not both.
* @since 4.1.5
* @see #modules(List)
* @see com.fasterxml.jackson.databind.Module
*/
public Jackson2ObjectMapperBuilder modules(Module... modules) {
return modules(Arrays.asList(modules));
}
/**
* Set a complete list of modules to be registered with the {@link ObjectMapper}.
* <p>Note: If this is set, no finding of modules is going to happen - not by
@ -344,24 +411,46 @@ public class Jackson2ObjectMapperBuilder {
* As a consequence, specifying an empty list here will suppress any kind of
* module detection.
* <p>Specify either this or {@link #modulesToInstall}, not both.
* @see #modules(Module...)
* @see com.fasterxml.jackson.databind.Module
*/
public Jackson2ObjectMapperBuilder modules(List<Module> modules) {
this.modules = new LinkedList<Module>(modules);
this.findModulesViaServiceLoader = false;
this.findWellKnownModules = false;
return this;
}
/**
* Specify one or more modules by class (or class name in XML),
* to be registered with the {@link ObjectMapper}.
* <p>Modules specified here will be registered in combination with
* Specify one or more modules to be registered with the {@link ObjectMapper}.
* <p>Modules specified here will be registered after
* Spring's autodetection of JSR-310 and Joda-Time, or Jackson's
* finding of modules (see {@link #findModulesViaServiceLoader}).
* finding of modules (see {@link #findModulesViaServiceLoader}),
* allowing to eventually override their configuration.
* <p>Specify either this or {@link #modules}, not both.
* @since 4.1.5
* @see com.fasterxml.jackson.databind.Module
*/
public Jackson2ObjectMapperBuilder modulesToInstall(Module... modules) {
this.modules = Arrays.asList(modules);
this.findWellKnownModules = true;
return this;
}
/**
* Specify one or more modules by class to be registered with
* the {@link ObjectMapper}.
* <p>Modules specified here will be registered after
* Spring's autodetection of JSR-310 and Joda-Time, or Jackson's
* finding of modules (see {@link #findModulesViaServiceLoader}),
* allowing to eventually override their configuration.
* <p>Specify either this or {@link #modules}, not both.
* @see #modulesToInstall(Module...)
* @see com.fasterxml.jackson.databind.Module
*/
public Jackson2ObjectMapperBuilder modulesToInstall(Class<? extends Module>... modules) {
this.modulesToInstall = modules;
this.moduleClasses = modules;
this.findWellKnownModules = true;
return this;
}
@ -444,9 +533,35 @@ public class Jackson2ObjectMapperBuilder {
public void configure(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper must not be null");
if (this.findModulesViaServiceLoader) {
// Jackson 2.2+
objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader));
}
else if (this.findWellKnownModules) {
registerWellKnownModulesIfAvailable(objectMapper);
}
if (this.modules != null) {
for (Module module : this.modules) {
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
objectMapper.registerModule(module);
}
}
if (this.moduleClasses != null) {
for (Class<? extends Module> module : this.moduleClasses) {
objectMapper.registerModule(BeanUtils.instantiate(module));
}
}
if (this.dateFormat != null) {
objectMapper.setDateFormat(this.dateFormat);
}
if (this.locale != null) {
objectMapper.setLocale(this.locale);
}
if (this.timeZone != null) {
objectMapper.setTimeZone(this.timeZone);
}
if (this.annotationIntrospector != null) {
objectMapper.setAnnotationIntrospector(this.annotationIntrospector);
@ -468,29 +583,6 @@ public class Jackson2ObjectMapperBuilder {
configureFeature(objectMapper, feature, this.features.get(feature));
}
if (this.modules != null) {
// Complete list of modules given
for (Module module : this.modules) {
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
objectMapper.registerModule(module);
}
}
else {
// Combination of modules by class names specified and class presence in the classpath
if (this.modulesToInstall != null) {
for (Class<? extends Module> module : this.modulesToInstall) {
objectMapper.registerModule(BeanUtils.instantiate(module));
}
}
if (this.findModulesViaServiceLoader) {
// Jackson 2.2+
objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader));
}
else {
registerWellKnownModulesIfAvailable(objectMapper);
}
}
if (this.propertyNamingStrategy != null) {
objectMapper.setPropertyNamingStrategy(this.propertyNamingStrategy);
}

View File

@ -19,7 +19,9 @@ package org.springframework.http.converter.json;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
@ -171,6 +173,24 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
this.builder.simpleDateFormat(format);
}
/**
* Override the default {@link Locale} to use for formatting.
* Default value used is {@link Locale#getDefault()}.
* @since 4.1.5
*/
public void setLocale(Locale locale) {
this.builder.locale(locale);
}
/**
* Override the default {@link TimeZone} to use for formatting.
* Default value used is UTC (NOT local timezone).
* @since 4.1.5
*/
public void setTimeZone(TimeZone timeZone) {
this.builder.timeZone(timeZone);
}
/**
* Set an {@link AnnotationIntrospector} for both serialization and deserialization.
*/
@ -316,11 +336,12 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
}
/**
* Specify one or more modules by class (or class name in XML),
* Specify one or more modules by class (or class name in XML)
* to be registered with the {@link ObjectMapper}.
* <p>Modules specified here will be registered in combination with
* <p>Modules specified here will be registered after
* Spring's autodetection of JSR-310 and Joda-Time, or Jackson's
* finding of modules (see {@link #setFindModulesViaServiceLoader}).
* finding of modules (see {@link #setFindModulesViaServiceLoader}),
* allowing to eventually override their configuration.
* <p>Specify either this or {@link #setModules}, not both.
* @since 4.0.1
* @see com.fasterxml.jackson.databind.Module

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -16,16 +16,22 @@
package org.springframework.http.converter.json;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
@ -35,6 +41,7 @@ import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory;
@ -42,16 +49,20 @@ import com.fasterxml.jackson.databind.deser.Deserializers;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.module.SimpleSerializers;
import com.fasterxml.jackson.databind.ser.BasicSerializerFactory;
import com.fasterxml.jackson.databind.ser.Serializers;
import com.fasterxml.jackson.databind.ser.std.ClassSerializer;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Test;
import org.springframework.beans.FatalBeanException;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
@ -167,15 +178,94 @@ public class Jackson2ObjectMapperBuilderTests {
}
@Test
public void setModules() {
public void localeSetter() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().locale(Locale.FRENCH).build();
assertEquals(Locale.FRENCH, objectMapper.getSerializationConfig().getLocale());
assertEquals(Locale.FRENCH, objectMapper.getDeserializationConfig().getLocale());
}
@Test
public void timeZoneSetter() {
TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris");
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().timeZone(timeZone).build();
assertEquals(timeZone, objectMapper.getSerializationConfig().getTimeZone());
assertEquals(timeZone, objectMapper.getDeserializationConfig().getTimeZone());
}
@Test
public void timeZoneStringSetter() {
String zoneId = "Europe/Paris";
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().timeZone(zoneId).build();
TimeZone timeZone = TimeZone.getTimeZone(zoneId);
assertEquals(timeZone, objectMapper.getSerializationConfig().getTimeZone());
assertEquals(timeZone, objectMapper.getDeserializationConfig().getTimeZone());
}
@Test(expected = IllegalArgumentException.class)
public void wrongTimeZoneStringSetter() {
String zoneId = "foo";
Jackson2ObjectMapperBuilder.json().timeZone(zoneId).build();
}
@Test
public void modules() {
NumberSerializer serializer1 = new NumberSerializer();
SimpleModule module = new SimpleModule();
module.addSerializer(Integer.class, serializer1);
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(Arrays.asList(new Module[]{module})).build();
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(module).build();
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null) == serializer1);
}
@Test
public void modulesToInstallByClass() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modulesToInstall(CustomIntegerModule.class).build();
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null).getClass() == CustomIntegerSerializer.class);
}
@Test
public void modulesToInstallByInstance() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modulesToInstall(new CustomIntegerModule()).build();
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null).getClass() == CustomIntegerSerializer.class);
}
@Test
public void defaultModules() throws JsonProcessingException, UnsupportedEncodingException {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
Long timestamp = 1322903730000L;
DateTime dateTime = new DateTime(timestamp, DateTimeZone.UTC);
assertEquals(timestamp.toString(), new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
}
@Test // SPR-12634
public void customizeDefaultModulesWithModule() throws JsonProcessingException, UnsupportedEncodingException {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.modulesToInstall(new CustomIntegerModule()).build();
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
}
@Test // SPR-12634
public void customizeDefaultModulesWithModuleClass() throws JsonProcessingException, UnsupportedEncodingException {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modulesToInstall(CustomIntegerModule.class).build();
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
}
@Test // SPR-12634
public void customizeDefaultModulesWithSerializer() throws JsonProcessingException, UnsupportedEncodingException {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.serializerByType(Integer.class, new CustomIntegerSerializer()).build();
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
}
private static SerializerFactoryConfig getSerializerFactoryConfig(ObjectMapper objectMapper) {
return ((BasicSerializerFactory) objectMapper.getSerializerFactory()).getFactoryConfig();
}
@ -196,6 +286,7 @@ public class Jackson2ObjectMapperBuilderTests {
public void serializerByType() {
JsonSerializer<Number> serializer = new NumberSerializer();
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.modules(new ArrayList<>()) // Disable well-known modules detection
.serializerByType(Boolean.class, serializer).build();
assertTrue(getSerializerFactoryConfig(objectMapper).hasSerializers());
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
@ -206,6 +297,7 @@ public class Jackson2ObjectMapperBuilderTests {
public void deserializerByType() throws JsonMappingException {
JsonDeserializer<Date> deserializer = new DateDeserializers.DateDeserializer();
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.modules(new ArrayList<>()) // Disable well-known modules detection
.deserializerByType(Date.class, deserializer).build();
assertTrue(getDeserializerFactoryConfig(objectMapper).hasDeserializers());
Deserializers deserializers = getDeserializerFactoryConfig(objectMapper).deserializers().iterator().next();
@ -249,6 +341,7 @@ public class Jackson2ObjectMapperBuilderTests {
JsonSerializer<Number> serializer2 = new NumberSerializer();
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json()
.modules(new ArrayList<>()) // Disable well-known modules detection
.serializers(serializer1)
.serializersByType(Collections.<Class<?>, JsonSerializer<?>>singletonMap(Boolean.class, serializer2))
.deserializersByType(deserializerMap)
@ -311,4 +404,36 @@ public class Jackson2ObjectMapperBuilderTests {
assertTrue(xmlObjectMapper.getClass().isAssignableFrom(XmlMapper.class));
}
public static class CustomIntegerModule extends Module {
@Override
public String getModuleName() {
return this.getClass().getSimpleName();
}
@Override
public Version version() {
return Version.unknownVersion();
}
@Override
public void setupModule(SetupContext context) {
SimpleSerializers serializers = new SimpleSerializers();
serializers.addSerializer(Integer.class, new CustomIntegerSerializer());
context.addSerializers(serializers);
}
}
public static class CustomIntegerSerializer extends JsonSerializer<Integer> {
@Override
public void serialize(Integer value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeNumberField("customid", value);
gen.writeEndObject();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -16,16 +16,23 @@
package org.springframework.http.converter.json;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
@ -34,23 +41,28 @@ import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers.DateDeserializer;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.module.SimpleSerializers;
import com.fasterxml.jackson.databind.ser.BasicSerializerFactory;
import com.fasterxml.jackson.databind.ser.Serializers;
import com.fasterxml.jackson.databind.ser.std.ClassSerializer;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.FatalBeanException;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
@ -58,6 +70,7 @@ import static org.junit.Assert.*;
*
* @author <a href="mailto:dmitry.katsubo@gmail.com">Dmitry Katsubo</a>
* @author Brian Clozel
* @author Sebastien Deleuze
*/
public class Jackson2ObjectMapperFactoryBeanTests {
@ -161,13 +174,57 @@ public class Jackson2ObjectMapperFactoryBeanTests {
assertEquals(dateFormat, this.factory.getObject().getDeserializationConfig().getDateFormat());
}
@Test
public void localeSetter() {
this.factory.setLocale(Locale.FRENCH);
this.factory.afterPropertiesSet();
assertEquals(Locale.FRENCH, this.factory.getObject().getSerializationConfig().getLocale());
assertEquals(Locale.FRENCH, this.factory.getObject().getDeserializationConfig().getLocale());
}
@Test
public void timeZoneSetter() {
TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris");
this.factory.setTimeZone(timeZone);
this.factory.afterPropertiesSet();
assertEquals(timeZone, this.factory.getObject().getSerializationConfig().getTimeZone());
assertEquals(timeZone, this.factory.getObject().getDeserializationConfig().getTimeZone());
}
@Test
public void timeZoneStringSetter() {
String zoneId = "Europe/Paris";
this.factory.setTimeZone(TimeZone.getTimeZone(zoneId));
this.factory.afterPropertiesSet();
TimeZone timeZone = TimeZone.getTimeZone(zoneId);
assertEquals(timeZone, this.factory.getObject().getSerializationConfig().getTimeZone());
assertEquals(timeZone, this.factory.getObject().getDeserializationConfig().getTimeZone());
}
@Test
public void wrongTimeZoneStringSetter() {
String zoneId = "foo";
this.factory.setTimeZone(TimeZone.getTimeZone(zoneId));
this.factory.afterPropertiesSet();
TimeZone timeZone = TimeZone.getTimeZone("GMT");
assertEquals(timeZone, this.factory.getObject().getSerializationConfig().getTimeZone());
assertEquals(timeZone, this.factory.getObject().getDeserializationConfig().getTimeZone());
}
@Test
public void setModules() {
NumberSerializer serializer1 = new NumberSerializer();
SimpleModule module = new SimpleModule();
module.addSerializer(Integer.class, serializer1);
this.factory.setModules(Arrays.asList(new Module[] {module}));
this.factory.setModules(Arrays.asList(new Module[]{module}));
this.factory.afterPropertiesSet();
ObjectMapper objectMapper = this.factory.getObject();
@ -175,6 +232,41 @@ public class Jackson2ObjectMapperFactoryBeanTests {
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null) == serializer1);
}
@Test
public void defaultModules() throws JsonProcessingException, UnsupportedEncodingException {
this.factory.afterPropertiesSet();
ObjectMapper objectMapper = this.factory.getObject();
Long timestamp = 1322903730000L;
DateTime dateTime = new DateTime(timestamp, DateTimeZone.UTC);
assertEquals(timestamp.toString(), new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
}
@Test // SPR-12634
public void customizeDefaultModulesWithModuleClass() throws JsonProcessingException, UnsupportedEncodingException {
this.factory.setModulesToInstall(CustomIntegerModule.class);
this.factory.afterPropertiesSet();
ObjectMapper objectMapper = this.factory.getObject();
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
}
@Test // SPR-12634
public void customizeDefaultModulesWithSerializer() throws JsonProcessingException, UnsupportedEncodingException {
Map<Class<?>, JsonSerializer<?>> serializers = new HashMap<>();
serializers.put(Integer.class, new CustomIntegerSerializer());
this.factory.setSerializersByType(serializers);
this.factory.afterPropertiesSet();
ObjectMapper objectMapper = this.factory.getObject();
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
}
@Test
public void simpleSetup() {
this.factory.afterPropertiesSet();
@ -237,6 +329,7 @@ public class Jackson2ObjectMapperFactoryBeanTests {
JsonSerializer<Class<?>> serializer1 = new ClassSerializer();
JsonSerializer<Number> serializer2 = new NumberSerializer();
factory.setModules(new ArrayList<>()); // Disable well-known modules detection
factory.setSerializers(serializer1);
factory.setSerializersByType(Collections.<Class<?>, JsonSerializer<?>> singletonMap(Boolean.class, serializer2));
factory.setDeserializersByType(deserializers);
@ -304,4 +397,36 @@ public class Jackson2ObjectMapperFactoryBeanTests {
assertEquals(XmlMapper.class, this.factory.getObjectType());
}
public static class CustomIntegerModule extends Module {
@Override
public String getModuleName() {
return this.getClass().getSimpleName();
}
@Override
public Version version() {
return Version.unknownVersion();
}
@Override
public void setupModule(SetupContext context) {
SimpleSerializers serializers = new SimpleSerializers();
serializers.addSerializer(Integer.class, new CustomIntegerSerializer());
context.addSerializers(serializers);
}
}
public static class CustomIntegerSerializer extends JsonSerializer<Integer> {
@Override
public void serialize(Integer value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeNumberField("customid", value);
gen.writeEndObject();
}
}
}