Make Joda DateTime serialization format configurable

We allow the serialization format of dates to be configured using
spring.jackson.date-format. However, this property only applies to
java.util.Date instances and has no effect on a Joda DateTime.

This commit updates our auto-configuration for Jackson to allow
the format string that is used to  serialize a Joda DateTime to be
configured. A new property, spring.jackson.joda-date-time-format has
been introduced. When configured, it is used to configure the
serialization format for a Joda DateTime. When it is not configured,
we fall back to using spring.jackson.date-format. If this fails,
either because the format string is incompatible (unlikely) or because
the user's configured the fully-qualified name of a DateFormat class,
a warning is logged encouraging the use of
spring.jackson.joda-date-time-format.

Fixes gh-2225
This commit is contained in:
Andy Wilkinson 2015-02-12 15:53:53 +00:00
parent f11bcb9495
commit 201fb5e534
3 changed files with 86 additions and 0 deletions

View File

@ -25,6 +25,10 @@ import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
@ -47,6 +51,9 @@ 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.module.SimpleModule;
import com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer;
import com.fasterxml.jackson.datatype.joda.ser.JacksonJodaFormat;
/**
* Auto configuration for Jackson. The following auto-configuration will get applied:
@ -97,6 +104,50 @@ public class JacksonAutoConfiguration {
}
@Configuration
@ConditionalOnClass({ Jackson2ObjectMapperBuilder.class, DateTime.class,
DateTimeSerializer.class })
static class JodaDateTimeJacksonConfiguration {
private final Log log = LogFactory.getLog(JodaDateTimeJacksonConfiguration.class);
@Autowired
private JacksonProperties jacksonProperties;
@Bean
public Module jodaDateTimeSerializationModule() {
SimpleModule module = new SimpleModule();
JacksonJodaFormat jacksonJodaFormat = null;
if (this.jacksonProperties.getJodaDateTimeFormat() != null) {
jacksonJodaFormat = new JacksonJodaFormat(DateTimeFormat.forPattern(
this.jacksonProperties.getJodaDateTimeFormat()).withZoneUTC());
}
else if (this.jacksonProperties.getDateFormat() != null) {
try {
jacksonJodaFormat = new JacksonJodaFormat(DateTimeFormat.forPattern(
this.jacksonProperties.getDateFormat()).withZoneUTC());
}
catch (IllegalArgumentException ex) {
if (this.log.isWarnEnabled()) {
this.log.warn("spring.jackson.date-format could not be used to "
+ "configure formatting of Joda's DateTime. You may want "
+ "to configure spring.jackson.joda-date-time-format as "
+ "well.");
}
}
}
if (jacksonJodaFormat != null) {
module.addSerializer(DateTime.class, new DateTimeSerializer(
jacksonJodaFormat));
}
return module;
}
}
@Configuration
@ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
@EnableConfigurationProperties({ HttpMapperProperties.class, JacksonProperties.class })

View File

@ -43,6 +43,13 @@ public class JacksonProperties {
*/
private String dateFormat;
/**
* Joda date time format string (yyyy-MM-dd HH:mm:ss). If not configured,
* {@code date-format} will be used as a fallback if it is configured with a format
* string.
*/
private String jodaDateTimeFormat;
/**
* One of the constants on Jackson's PropertyNamingStrategy
* (CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES). Can also be a fully-qualified class
@ -83,6 +90,14 @@ public class JacksonProperties {
this.dateFormat = dateFormat;
}
public String getJodaDateTimeFormat() {
return this.jodaDateTimeFormat;
}
public void setJodaDateTimeFormat(String jodaDataTimeFormat) {
this.jodaDateTimeFormat = jodaDataTimeFormat;
}
public String getPropertyNamingStrategy() {
return this.propertyNamingStrategy;
}

View File

@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.Set;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import org.junit.After;
import org.junit.Before;
@ -134,6 +135,23 @@ public class JacksonAutoConfigurationTests {
"spring.jackson.date-format:yyyyMMddHHmmss");
this.context.refresh();
ObjectMapper mapper = this.context.getBean(ObjectMapper.class);
DateTime dateTime = new DateTime(1988, 6, 25, 20, 30, DateTimeZone.UTC);
assertEquals("\"19880625203000\"", mapper.writeValueAsString(dateTime));
dateTime = new DateTime(1988, 6, 25, 20, 30);
Date date = dateTime.toDate();
assertEquals("\"19880625203000\"", mapper.writeValueAsString(date));
}
@Test
public void customJodaDateTimeFormat() throws Exception {
this.context.register(JacksonAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jackson.date-format:yyyyMMddHHmmss",
"spring.jackson.joda-date-time-format:yyyy-MM-dd HH:mm:ss");
this.context.refresh();
ObjectMapper mapper = this.context.getBean(ObjectMapper.class);
DateTime dateTime = new DateTime(1988, 6, 25, 20, 30, DateTimeZone.UTC);
assertEquals("\"1988-06-25 20:30:00\"", mapper.writeValueAsString(dateTime));
Date date = new DateTime(1988, 6, 25, 20, 30).toDate();
assertEquals("\"19880625203000\"", mapper.writeValueAsString(date));
}
@ -147,6 +165,8 @@ public class JacksonAutoConfigurationTests {
"spring.jackson.date-format:org.springframework.boot.autoconfigure.jackson.JacksonAutoConfigurationTests.MyDateFormat");
this.context.refresh();
ObjectMapper mapper = this.context.getBean(ObjectMapper.class);
DateTime dateTime = new DateTime(1988, 6, 25, 20, 30, DateTimeZone.UTC);
assertEquals("\"1988-06-25T20:30:00.000Z\"", mapper.writeValueAsString(dateTime));
Date date = new DateTime(1988, 6, 25, 20, 30).toDate();
assertEquals("\"1988-06-25 20:30:00\"", mapper.writeValueAsString(date));
}