Spring-boot: spring.jackson.date-format configuration does not affect serialisation of Joda DateTime instances

Created on 23 Dec 2014  Â·  22Comments  Â·  Source: spring-projects/spring-boot

I have breakpoints set in both:
MappingJackson2HttpMessageConverterConfiguration.mappingJackson2HttpMessageConverter JacksonObjectMapperConfiguration.jacksonObjectMapper

I can see that there is an ObjectMapper bean being created because jacksonObjectMapper is never called and thus MappingJackson2HttpMessageConverterConfiguration uses this other ObjectMapper and not the one that I'm trying to configure. How do I tell what is creating that bean? From that logs, my guess is that it is HypermediaSupportBeanDefinitionRegistrar.registerBeanDefinitions that is doing it from:

        <dependency>
            <groupId>org.springframework.hateoas</groupId>
            <artifactId>spring-hateoas</artifactId>
        </dependency>

If I put a breakpoint there it is definitely called before MappingJackson2HttpMessageConverterConfiguration.mappingJackson2HttpMessageConverter but it is so hard to tell if this is the root of my problem. I can't remove that dependency because it is so pervasively used in my project that it would take me hours to refactor.

Most helpful comment

The configuration of write-dates-as-timestamps works correctly as far as I can tell, and it affects the serialisation of both java.util.Date and org.joda.time.DateTime instances. The problem is that when write-dates-as-timestamps is false spring.jackson.date-format only affects the serialisation of java.util.Date instances.

This configuration:

spring:
  jackson:
    date-format: "YYYY-MM-dd"
    serialization:
      write_dates_as_timestamps: true

Gives me this output:

{"date":1421343027885,"dateTime":1421343027895}

Note that both the java.util.Date and org.joda.time.DateTime instances have been serialised as timestamps.

On the other hand this configuration:

spring:
  jackson:
    date-format: "YYYY-MM-dd"
    serialization:
      write_dates_as_timestamps: false

Gives me this output:

{"date":"2015-01-15","dateTime":"2015-01-15T17:31:28.884Z"}

Note that both are now being formatted as strings but that only java.util.Date has obeyed the DateFormat configuration. This is due to the Jackson limitation that I linked to above.

I see this behaviour both with and without Spring HATEOAS on the classpath. If you're seeing behaviour that's different to what I've described above, then please describe your application's configuration and its dependencies in more detail. Ideally, please provide a sample that shows a DateTime always being serialised as a timestamp.

All 22 comments

FYI, I'm using 1.2.0.RELEASE

@csavory Can you provide some more information about what you're trying to configure and how you're trying to configure it please?

Any spring.jackson.* properties that you specify are applied to the auto-configured JackonObjectMapperBuilder. This builder is then used to configure the default ObjectMapper as well as those created by the Spring HATEOAS and Spring Data REST auto-configuration.

All I'm trying to do is get the Joda DateTime objects written out as Strings instead of timestamps. It works if I put the @JsonFormat(shape = Shape.STRING) annotation right on the property in the object, but it won't work if I try to configure the ObjectMapper holistically. This is because the ObjectMapper that is used is being defined somewhere else.

This is how I'm trying to configure:

  jackson:
    date-format: com.fasterxml.jackson.databind.util.ISO8601DateFormat
    serialization:
      write-dates-as-timestamps: false

@wilkinsona do I remove the waiting-for-feedback label now that I responded?

That helps, thanks. In investigating #2322 I'd been using java.util.Date. I'll take another look using DateTime this time.

@wilkinsona here is my configuration I'm using.

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-joda</artifactId>
        </dependency>
    @Bean
    public Module jodaModule() {
        return new JodaModule();
    }

You're hitting a limitation in Jackson. Jackson treats the date format configured on the ObjectMapper separately to the one used by the custom serialiser for Joda's DataTime. This is with good reason as you can't format a Joda DateTime with a standard java.text.DateFormat.

@wilkinsona I'm not sure I agree that writing it out as a string is a limitation of Joda. If you look at com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer line 34, there is a test to see whether it should write as a string or a timestamp based on the provider settings. The problem is that I can never get that statement to return false even though I am configuring my mapper via Spring Boot as write-dates-as-timestamps: false

if you look at com.fasterxml.jackson.datatype.joda.ser.JodaDateSerializerBase<T> on line 48, I can get it to write as a string if I set the property's annotation correctly.

The problem is that I want to do this globally through the ObjectMapper, but something is preventing Spring Boot from configuring the ObjectMapper as JacksonAutoConfiguration.JacksonObjectMapperConfiguration.jacksonObjectMapper is never called in my environment.

The problem is that I want to do this globally through the ObjectMapper, but something is preventing Spring Boot from configuring the ObjectMapper as JacksonAutoConfiguration.JacksonObjectMapperConfiguration.jacksonObjectMapper is never called in my environment.

The configuration of write-dates-as-timestamps works correctly as far as I can tell, and it affects the serialisation of both java.util.Date and org.joda.time.DateTime instances. The problem is that when write-dates-as-timestamps is false spring.jackson.date-format only affects the serialisation of java.util.Date instances.

This configuration:

spring:
  jackson:
    date-format: "YYYY-MM-dd"
    serialization:
      write_dates_as_timestamps: true

Gives me this output:

{"date":1421343027885,"dateTime":1421343027895}

Note that both the java.util.Date and org.joda.time.DateTime instances have been serialised as timestamps.

On the other hand this configuration:

spring:
  jackson:
    date-format: "YYYY-MM-dd"
    serialization:
      write_dates_as_timestamps: false

Gives me this output:

{"date":"2015-01-15","dateTime":"2015-01-15T17:31:28.884Z"}

Note that both are now being formatted as strings but that only java.util.Date has obeyed the DateFormat configuration. This is due to the Jackson limitation that I linked to above.

I see this behaviour both with and without Spring HATEOAS on the classpath. If you're seeing behaviour that's different to what I've described above, then please describe your application's configuration and its dependencies in more detail. Ideally, please provide a sample that shows a DateTime always being serialised as a timestamp.

Andy, thanks for the info. It'll take me a couple of days to put together a sample for you. Is there a place I check it into or is just my repo fine?

Sent from my iPhone

On Jan 15, 2015, at 10:37 AM, Andy Wilkinson [email protected] wrote:

The configuration of write-dates-as-timestamps works correctly as far as I can tell, and it affects the serialisation of both java.util.Date and org.joda.time.DateTime instances. The problem is that when write-dates-as-timestamps is false spring.jackson.date-format only affects the serialisation of java.util.Date instances.

This configuration:

spring:
jackson:
date-format: "YYYY-MM-dd"
serialization:
write_dates_as_timestamps: true
Gives me this output:

{"date":1421343027885,"dateTime":1421343027895}
Note that both the java.util.Data and org.joda.time.DateTime instances have been serialised as timestamps.

On the other hand this configuration:

spring:
jackson:
date-format: "YYYY-MM-dd"
serialization:
write_dates_as_timestamps: false
Gives me this output:

{"date":"2015-01-15","dateTime":"2015-01-15T17:31:28.884Z"}
Note that both are now being formatted as strings but that only java.util.Date has obeyed the DateFormat configuration. This is due to the Jackson limitation that I linked to above.

I see this behaviour both with and without Spring HATEOAS on the classpath. If you're seeing behaviour that's different to what I've described above, then please describe your application's configuration and its dependencies in more detail. Ideally, please provide a sample that shows a DateTime always being serialised as a timestamp.

—
Reply to this email directly or view it on GitHub.

Thanks. Either something in your own repo, or as a PR against Spring Boot issues is fine.

Thanks.

You've annotated your configuration class with @EnableHypermediaSupport so you've opted to configure Spring HATEOAS yourself. Crucially, this means that you've disabled the logic which applies your spring.jackson.* configuration to the _halObjectMapper bean. Removing this annotation will mean that the configuration takes effect. You should also get rid of your ObjectMapper and JodaModule beans and allow Boot to automatically configure them for you.

With these three changes your sample app produces this output:

{"date":"2015-01-22T10:44:06Z","jodaDate":"2015-01-22T10:44:06.911Z","jodaDateForceAsString":"2015-01-22T10:44:06.913Z"}

I've reopened this as I'd like to address the Jackson limitation that stops spring.jackson.date-format from affecting the serialisation of a Joda DateTime. This'll require a change in Spring Framework.

Thank you @wilkinsona
Your suggestions work for me.

When we started using Spring Boot (v0.5), HypermediaAutoConfiguration was not part of Boot so that was how it was configured for us.

By the way, there is no documentation in the Boot docs about how to configure HATEOAS support with Boot. The only docs are found at https://github.com/spring-projects/spring-hateoas/blob/master/readme.md
I only mention that since it might help the next person trying to configure HATEOAS if it was mentioned on the Spring Boot reference page.

You don't really need to do anything to configure HATEOAS support other than having the right things on the classpath, i.e. there's not much to say in the manual that wouldn't end up being a duplication of Spring HATEOAS's existing documentation. I've just added a starter to help with the classpath part. We also have a sample application that shows Spring HATEOAS in action with Spring Boot.

I agree with you that you need to do anything to configure HATEOAS support for Boot.

But the configuration is different if I am using:
Spring Framework + Spring HATEOAS
vs.
Spring Framework + Spring HATEOAS + Spring Boot

I would think since there is a difference in how you configure it the latter scenario (even if it's a negative configuration) it would be worth mentioning in the Boot reference. If it was there in the reference, I would have never created this issue b/c I would have read it there and removed my @EnableHypermediaSupport annotation.

As I said before, just trying to help folks out coming after me and don't look through the specifics of how all the AutoConfigurators work by looking at the code.

As I said before, just trying to help folks out coming after me and don't look through the specifics of how all the AutoConfigurators work by looking at the code.

@wilkinsona this solution stops working when I merged in my temporary solution for #2360 where my WebConfig extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter. I now have an ObjectMapper that is created without Jackson2ObjectMapperBuilder and I cannot figure out who is creating that. I can tell it isn't created with Jackson2ObjectMapperBuilder because it doesn't have the Joda module serializers in its SerializerFactory that are created in Jackson2ObjectMapperBuilder.registerWellKnownModulesIfAvailable.

I don't see any ObjectMapper stuff in `WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter. Could extending that class be preventingJacksonAutoConfiguration`` from doing something it needs?

@wilkinsona nevermind. I found a way to put our WebConfig back to

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter implements EnvironmentAware {

It's working again.

@csavory You make a good point about the Spring HATEOAS auto-configuration. Thanks. I've opened #2426 to improve the docs.

I have the same problem as @csavory described - if I add a WebConfig that extends WebMvcConfigurerAdapter then my date format specified for jackson in application.properties is not taking effect:

spring.jackson.date-format=yyyy-MM-dd'T'HH:mm:ss.SSSZ
spring.jackson.serialization.write_dates_as_timestamps=false

I'm using boot spring boot 1.4.0. Does my WebConfig need to implement EnvironmentAware? @csavory, can you provide an example?

P.S. I'm using WebConfig to add resource handlers for swagger.

@pavelfomin this issue is closed. If you believe you've found an issue with a recent version of Spring Boot, please create separate issue. Consider using stackoverflow or gitter for questions.

Was this page helpful?
0 / 5 - 0 ratings