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
pull/2150/merge
Andy Wilkinson 10 years ago
parent f11bcb9495
commit 201fb5e534

@ -25,6 +25,10 @@ import java.util.Map.Entry;
import javax.annotation.PostConstruct; 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.BeanUtils;
import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory; 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.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.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: * 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 @Configuration
@ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class }) @ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
@EnableConfigurationProperties({ HttpMapperProperties.class, JacksonProperties.class }) @EnableConfigurationProperties({ HttpMapperProperties.class, JacksonProperties.class })

@ -43,6 +43,13 @@ public class JacksonProperties {
*/ */
private String dateFormat; 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 * One of the constants on Jackson's PropertyNamingStrategy
* (CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES). Can also be a fully-qualified class * (CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES). Can also be a fully-qualified class
@ -83,6 +90,14 @@ public class JacksonProperties {
this.dateFormat = dateFormat; this.dateFormat = dateFormat;
} }
public String getJodaDateTimeFormat() {
return this.jodaDateTimeFormat;
}
public void setJodaDateTimeFormat(String jodaDataTimeFormat) {
this.jodaDateTimeFormat = jodaDataTimeFormat;
}
public String getPropertyNamingStrategy() { public String getPropertyNamingStrategy() {
return this.propertyNamingStrategy; return this.propertyNamingStrategy;
} }

@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime; import org.joda.time.LocalDateTime;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@ -134,6 +135,23 @@ public class JacksonAutoConfigurationTests {
"spring.jackson.date-format:yyyyMMddHHmmss"); "spring.jackson.date-format:yyyyMMddHHmmss");
this.context.refresh(); this.context.refresh();
ObjectMapper mapper = this.context.getBean(ObjectMapper.class); 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(); Date date = new DateTime(1988, 6, 25, 20, 30).toDate();
assertEquals("\"19880625203000\"", mapper.writeValueAsString(date)); assertEquals("\"19880625203000\"", mapper.writeValueAsString(date));
} }
@ -147,6 +165,8 @@ public class JacksonAutoConfigurationTests {
"spring.jackson.date-format:org.springframework.boot.autoconfigure.jackson.JacksonAutoConfigurationTests.MyDateFormat"); "spring.jackson.date-format:org.springframework.boot.autoconfigure.jackson.JacksonAutoConfigurationTests.MyDateFormat");
this.context.refresh(); this.context.refresh();
ObjectMapper mapper = this.context.getBean(ObjectMapper.class); 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(); Date date = new DateTime(1988, 6, 25, 20, 30).toDate();
assertEquals("\"1988-06-25 20:30:00\"", mapper.writeValueAsString(date)); assertEquals("\"1988-06-25 20:30:00\"", mapper.writeValueAsString(date));
} }

Loading…
Cancel
Save