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:
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…
Reference in New Issue