Jackson SerializationFeature.WRITE_DATES_AS_TIMESTAMPS not turning off timestamps in spring
Asked Answered
A

6

28

After a lot of searching I tracked down how to stop java.util.Date fields from being serialised into timestamps when converting to JSON responses in my @RestController.

However I cannot get it to work. All the posts I found said to disable the SerializationFeature.WRITE_DATES_AS_TIMESTAMPS feature of the Jackson objet mapper. So I wrote the following code:

public class MVCConfig {

    @Autowired
    Jackson2ObjectMapperFactoryBean objectMapper;

    @PostConstruct
    public void postConstruct() {
        this.objectMapper.setFeaturesToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}

As I understand it, a config is a bean as well so auto wiring in the object mapper to set additional properties should work. I've used break points and everything looks good with this setup.

However when I serialise a bean with a java.util.Date property in a response to a http query, I'm still getting a time stamp.

Does anyone know why this is not working? It's got me stumped !

Alleyn answered 16/1, 2015 at 6:56 Comment(3)
I've just figured something out. I'm creating a object mapper bean in the base context so that my back end services can use it to deserialise JSON coming from external servers. This bean is being picked up by the MVC code above. However when I serialise a response to a request, a message converter is used instead. So I'll have to figure out how to address that bean rather that the object mapper bean being used by my back end.Alleyn
Reference: fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/…Matless
@OndraŽižka Be aware that Jackson's reference is misleading in this case, because Spring Boot reconfigures some Mapper features. Thus the defaults mentioned in Jackson's reference don't necessarily apply in a Spring Boot app.Finesse
A
43

After lots of messing around I found that the following code fixed the problem:

public class MVCConfig extends WebMvcConfigurerAdapter {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { 
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter jsonMessageConverter = (MappingJackson2HttpMessageConverter) converter;
                ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
                objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
                break;
            }
        }
    }
}

I'm not sure if there is an easier way to access the Jackson MVC message converter and configure it. But this is working for me.

Alleyn answered 19/1, 2015 at 0:43 Comment(7)
Not working for me, although the code is executed on startup. Did you use any xml configs as well? Maybe it's not possible to mix them?Sabrina
Nope. My app is a 100% pure java app.Alleyn
And delete <mvc:annotation-driven/> tag from your xml configJenaejenda
Could've also converters.removeIf(c -> c instanceof MappingJackson2HttpMessageConverter); converters.add(mappingJackson2HttpMessageConverter());.Wellwisher
Adding spring.jackson.serialization.write-dates-as-timestamps=false to properties file was enough for me.Bellinzona
It works for me! The version of Spring Boot I am using is 2.1.9.RELEASE.Hoashis
I used the same to turn them on. Dont know why the nanoseconds resolution that is enabled by default was no working and I had to use a .enable() istead of a .disable()Laomedon
S
8

Yes I agree with @Feyyaz: adding config to the properties/yml file is the way to config the default, contextual (de)serializer, when this is out of your control and only left to be manipulated by Spring context.

See this part of Spring documentation for more details:

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper

Citation in case of original link removal:

79.3 Customize the Jackson ObjectMapper

Spring MVC (client and server side) uses HttpMessageConverters to negotiate content conversion in an HTTP exchange. If Jackson is on the classpath, you already get the default converter(s) provided by Jackson2ObjectMapperBuilder, an instance of which is auto-configured for you. The ObjectMapper (or XmlMapper for Jackson XML converter) instance (created by default) has the following customized properties:

  • MapperFeature.DEFAULT_VIEW_INCLUSION is disabled
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is disabled
  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS is disabled

Spring Boot also has some features to make it easier to customize this behavior. You can configure the ObjectMapper and XmlMapper instances by using the environment. Jackson provides an extensive suite of simple on/off features that can be used to configure various aspects of its processing. These features are described in six enums:

Enum Property Values
com.fasterxml.jackson.databind.DeserializationFeature spring.jackson.deserialization.<feature_name> true, false
com.fasterxml.jackson.core.JsonGenerator.Feature spring.jackson.generator.<feature_name> true, false
com.fasterxml.jackson.databind.MapperFeature spring.jackson.mapper.<feature_name> true, false
com.fasterxml.jackson.core.JsonParser.Feature spring.jackson.parser.<feature_name> true, false
com.fasterxml.jackson.databind.SerializationFeature spring.jackson.serialization.<feature_name> true, false
com.fasterxml.jackson.annotation.JsonInclude.Include spring.jackson.default-property-inclusion always, non_null, non_absent, non_default, non_empty

For example, to enable pretty print, set spring.jackson.serialization.indent_output=true. Note that, thanks to the use of relaxed binding, the case of indent_output does not have to match the case of the corresponding enum constant, which is INDENT_OUTPUT.

This environment-based configuration is applied to the auto-configured Jackson2ObjectMapperBuilder bean and applies to any mappers created by using the builder, including the auto-configured ObjectMapper bean.

The context’s Jackson2ObjectMapperBuilder can be customized by one or more Jackson2ObjectMapperBuilderCustomizer beans. Such customizer beans can be ordered (Boot’s own customizer has an order of 0), letting additional customization be applied both before and after Boot’s customization.

Any beans of type com.fasterxml.jackson.databind.Module are automatically registered with the auto-configured Jackson2ObjectMapperBuilder and are applied to any ObjectMapper instances that it creates. This provides a global mechanism for contributing custom modules when you add new features to your application.

If you want to replace the default ObjectMapper completely, either define a @Bean of that type and mark it as @Primary or, if you prefer the builder-based approach, define a Jackson2ObjectMapperBuilder @Bean. Note that, in either case, doing so disables all auto-configuration of the ObjectMapper. If you provide any @Beans of type MappingJackson2HttpMessageConverter, they replace the default value in the MVC configuration. Also, a convenience bean of type HttpMessageConverters is provided (and is always available if you use the default MVC configuration). It has some useful methods to access the default and user-enhanced message converters.

See the “Section 79.4, “Customize the @ResponseBody Rendering”” section and the WebMvcAutoConfiguration source code for more details.

Examples:

    spring:
      jackson:
        default-property-inclusion: non_null # to exclude null in json serialization
        serialization:
          write-dates-as-timestamps: true # write milliseconds since epoch in the final json

Or:

    spring.jackson.default-property-inclusion: non_null # to exclude null in json serialization
    spring.jackson.serialization.write-dates-as-timestamps: true # write milliseconds since epoch in the final json
Smoot answered 13/12, 2018 at 10:42 Comment(0)
H
2
ObjectMapper objectMapper = new ObjectMapper()
                .registerModule(new JavaTimeModule())
                .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Heron answered 22/3, 2019 at 1:22 Comment(1)
Please explain your code - here's whyMartelli
G
1

Yes, to hook in and change the object mapper that the converter is using you should do something like

public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
        this.configure(com.fasterxml.jackson.databind.SerializationFeature.
                WRITE_DATES_AS_TIMESTAMPS, false);
    }
}

and inside your MVCConfig

@Bean
public ObjectMapper jacksonObjectMapper() {
    return new CustomObjectMapper();
}

@Bean
public SerializationConfig serializationConfig() {
    return jacksonObjectMapper().getSerializationConfig();
}
Guernica answered 16/1, 2015 at 10:27 Comment(2)
Hmm. I was hoping there would be a way to create the serialisation bean in the back end config and have the webMvc just use it or automatically add it to the list of converters. I'll give this a try and see what happens.Alleyn
Couldn't get that to work. See my solution below. Thanks anyway.Alleyn
H
1

If you are using swagger with spring boot and your date is Date is always getting serialised as long, 
and SerializationFeature.WRITE_DATES_AS_TIMESTAMPS & spring.jackson.serialization.write-dates-as-timestamps=false are not helping, below solution worked for me. Add it to your class annotated with @SpringBootApplication:

@Autowired
private RequestMappingHandlerAdapter handlerAdapter;

@EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
    handlerAdapter
            .getMessageConverters()
            .stream()
            .forEach(c -> {
                if (c instanceof MappingJackson2HttpMessageConverter) {
                    MappingJackson2HttpMessageConverter jsonMessageConverter = (MappingJackson2HttpMessageConverter) c;
                    ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
                    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
                }
            });
}

Actual issue: SerializationFeature.WRITE_DATES_AS_TIMESTAMPS value is not read from spring configuration file which need to be set false in order 
to covert long value while serialisation.

Hux answered 5/9, 2021 at 18:4 Comment(0)
E
1

I had the same problem, in a Spring Boot 3 project. After investigation, it turned out that I had a configuration WebMvcConfigurer class (for tuning CORS settings), annotated as @EnableWebMvc. Like below:

@Configuration
// wrong
// @EnableWebMvc
class ApplicationWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        ...
    }

}

Note: it looks incorrect to mark such configuration class with @EnableWebMvc. It is not required for the WebMvcConfigurer to apply its effects. And in fact, it resets some default Spring Boot settings. Including preferences of the Jackson ObjectMapper object used by MappingJackson2HttpMessageConverter - i.e. turned off SerializationFeature.WRITE_DATES_AS_TIMESTAMPS.

Removing this annotation fixed Jackson serialization behavior for me.

Erland answered 6/10, 2023 at 14:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.