4

We had two application on spring boot. One was spring rest api based & second was spring MVC based.

We have megred both the application due to some business reasons as the context was the same and everything is working fine except java.time.LocalDateTime formatting that does by spring automatically on rest API. previously it was formatting LocalDateTime as "2018-08-30T18:13:24" but after merging it is showing as [ 2018, 08, 30, 18, 13, 24 ],

I have found out @EnableWebMVC annotation is the culprit but after removing that annotation web-mvc pages do not work.

What should I do so that date display in ISO (String) format and view resolver & jsp pages works fine?

Please help thanks.

1
  • I wasted almost a whole day looking at different Jackson configuration alternatives and nothing was working. Thankfully I stumbled upon your post and indeed @EnableWebMvc was the culprit. Commented Nov 23, 2021 at 19:17

4 Answers 4

10

Everyone is saying @EnableWebMvc is the culprit. But, no one is saying with WebMvc how to resolve this issue.

So, to answer the question, yes, there is a way to resolve this issue by not removing the @EnableWebMvc.

Before moving into the answer, let's understand a few concepts:

  • HttpMessageConverters -> These are the ones that convert Java Objects from and to JSON/XML.
  • By default, Spring boot will add the following converters:
    1. ByteArrayHttpMessageConverter
    2. StringHttpMessageConverter
    3. ResourceHttpMessageConverter
    4. SourceHttpMessageConverter
    5. FormHttpMessageConverter
    6. Jaxb2RootElementHttpMessageConverter
    7. MappingJackson2XmlHttpMessageConverter
    8. MappingJackson2HttpMessageConverter
  • So, whenever we are converting the java object into JSON, Spring will go through this list of converters one by one in order and picks the relevant one to use for conversion.
  • Now, if we add our custom MappingJackson2HttpMessageConverter to this list as the last element, then Spring will not come to it because before reaching our converter(9th element), there is the default converter at the 7th index.
  • So, to resolve this issue, we need to remove the default MappingJackson2HttpMessageConverter and we need to add our custom converter.
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//        Remove the default MappingJackson2HttpMessageConverter
        converters.removeIf(converter -> {
            String converterName = converter.getClass().getSimpleName();
            return converterName.equals("MappingJackson2HttpMessageConverter");
        });
//        Add your custom MappingJackson2HttpMessageConverter
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        converter.setObjectMapper(objectMapper);
        converters.add(converter);
        WebMvcConfigurer.super.extendMessageConverters(converters);
    }
}

Note: Please don't use te configureMessageConverters() method instead of the extendMessageConverters() method from WebMvcConfigurer, because the configure method will remove all the existing converters are installed by default.

Hope it will help someone like me who has wasted some hours debugging the issue :)

Sign up to request clarification or add additional context in comments.

2 Comments

Hats off to you and to your professional and scientific explanation. After struggling for hours, this is by far the one-shot perfect solution
perfect, this is what i need. thanks a lot
0

If you are using Jackson as your JSON <-> POJO mapper, you can set the following properties:

spring:
  jackson:
    date-format: yyyy-MM-dd'T'hh:mm:ss
    serialization:
      write-dates-as-timestamps: false

spring.jackson.serialization.write-dates-as-timestamps defaults to true, which serializes LocalDateTime as an array, like the one you show.

For a finer-grained control, you can also annotate date-time fields like following:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'hh:mm:ss")
private LocalDateTime datetime;

It takes precedence over the above property.

Take a look at other JSON related Spring Boot properties here: https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#json-properties

2 Comments

Only this @JsonFormat works but I don't want to go on every dtos and do it. I want to do it via centralize way.
Did you define your own ObjectMapper bean? If that's the case than this property won't work, but you still can set this configuration in your objectMapper with objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false).
0

For those still facing the issue, @EnableWebMVC is definitely the culprit as it disables the Web MVC auto-configuration default behaviors in favor of yours.

About that, the Spring Boot reference documentation says:

Spring MVC Auto-configuration
...
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc ...

However, you can add custom configuration without losing the default auto-configuration behaviors.

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

That said, removing @EnableWebMVC should work just fine.

Comments

0

Following the Mohan's answer I was able to simplify it for those configuring Jackson beans on their own. Having objectMapper bean in your config you can customize Jackson serialization features via JsonMapper.builder():

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(mappingJackson2HttpMessageConverter());
    }

    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        return new MappingJackson2HttpMessageConverter(objectMapper());
    }

    @Bean
    public ObjectMapper objectMapper() {
        return JsonMapper.builder()
                //Add all your modules
                .addModule(new Jdk8Module())
                .addModule(new JavaTimeModule())
                .addModule(new ParameterNamesModule())
                //Write dates and durations in textual representation instead of numeric
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false)
                .build();
    }
}

These two configure() calls below resolved the issue for me so I was getting following API responses:

  {
    "date": "2024-03-18T00:00:00",
    "duration": "PT0.000000075S"
  }

I was using Spring Framework but I believe you can achieve the same with Spring Boot even without calling configureMessageConverters (wasn't able to try it)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.