spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.45k stars 40.76k forks source link

Default Jackson HttpMessageConverter does not honor autoconfigured ObjectMapper #1620

Closed holgerstolzenberg closed 10 years ago

holgerstolzenberg commented 10 years ago

Please see

https://github.com/ewerk/sample_spring-boot-camel-mongo-jdk8/blob/features/jsr310/prototype-app/src/main/java/com/ewerk/prototype/api/ApiConfiguration.java

that illustrates the problem. The ApiConfiguration class does nothing special then overriding the HttpMessageConverters used by WebMVC. I am not configuring anything special to the Jackson ObjectMapper. The jackson JSR-310 module is on the classpath.

If you launch the app and access the URL http://localhost:9090/prototype/api/persons/all you can see that the person beans birthday property is correctly mapped to a string representation

Commenting out the code within ApiConfiguration and running the app again shows that the birthday property is rendered as a JSON object.

Debugging the app shows that all autoconfiguration related stuff is honoring the JSR-310 module, which then gets registered in the @PostConstruct method of JacksonAutoConfiguration. But something in bean creation order is messed up or the Jackson message converter created as default does not use the ObjectMapper instance created by spring boot autoconfiguration.

philwebb commented 10 years ago

I have been able to get the gradle eclipse plugin to work with your sample application so I have tried to create a simplified project to reproduce the problem.

Unfortunately, I've not been able to fully replicate the issue. If I run the application the com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer class does kick-in and the JSON is rendered correctly as three fields:

{
    "birthday": [
        2014,
        9,
        25
    ]
}

If I completely manage the HttpMessageConverters (see commented out code in Repro1620Application) then no custom serialization occurs:

{
    "birthday": {
        "year": 2014,
        "month": "SEPTEMBER",
        "dayOfMonth": 25,
        "dayOfWeek": "THURSDAY",
        "era": "CE",
        "dayOfYear": 268,
        "leapYear": false,
        "monthValue": 9,
        "chronology": {
            "id": "ISO",
            "calendarType": "iso8601"
        }
    }
}

What do you mean by "birthday property is correctly mapped to a string representation"? I'm not really sure which part of the JSON serialization process would cause this to happen. Could you update the issues project that I created to show me how you would expect the String representation to be marshaled?

holgerstolzenberg commented 10 years ago

Please excuse, my description was a bit unprecise. I mean exactly the behaviour you described. With the overridden mappers, the birthday property is (correctly) rendered in JSON:

{
    "birthday": [
        2014,
        9,
        25
    ]
}

Commenting out the override causes the birthday property to be rendered as JSON representation of the LocalDate class (incorrect).

The point is: Why are these overrides necessary? I do not change anything to the mappingJackson2HttpMessageConverter, because it is setup correctly by spring boot.

holgerstolzenberg commented 10 years ago

I have managed to reproduce the issue with your simplified sample application. Adding @EnableWebMvc breaks the JSON rendering behaviour. I have created a pull request at spring-boot-issues project that showcases that. I had to change the parent POM version to 1.1.6.RELEASE.

holgerstolzenberg commented 10 years ago

The above references are invalid as they have been hooked up because I accidentally committed a fix for gh-1619 on a branch for gh-1620. The invalid gh-1620 branch has been removed, therefore the above references are invalid. They can be deleted by someone with write access.

sdeleuze commented 10 years ago

Indeed, with your PR and additional explanations, I have been able to reproduce this behavior. In fact, when @EnableWebMvc is used, Spring Framework (and not Spring Boot) is responsible to create the message converters.

Up to Spring Framework 4.1.0, MappingJackson2HttpMessageConverter was using the default Jackson ObjectMapper constructor, which has not JSR-310 module automatically enabled.

Fortunately, the recently released Spring Framework 4.1.1 now uses Jackson2ObjectMapperBuilderto create the ObjectMapper and it is JSR-310 aware. Spring Boot 1.2.0.BUILD-SNAPSHOT already has the right behavior since it depends on the latest Spring Framework version and is Jackson2ObjectMapper aware (see commit 315213ea4e2fb3b4dd42c056b0a0da5f05d9f01a for more details).

So with Spring Boot 1.1.x, you should use @EnableAutoConfiguration without @EnableWebMvc in order to allow Spring Boot to autoconfigure your ObjectMapper instance. If you want to use @EnableWebMvc and still would like to have your ObjectMapper instance autoconfigured, you should specify <spring.version>4.1.1.RELEASE</spring.version> in your Maven properties.

hantsy commented 9 years ago

I used @EnableWebMvc on the WebConfig, and created a custome @Configuration for ObjectMapper, it does not work.

@Configuration
public class Jackson2ObjectMapperConfig {
    @Bean
    @Primary
    public ObjectMapper objectMapper() {

        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); 
        builder.serializationInclusion(Include.NON_EMPTY);
        builder.featuresToDisable(
                SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
                DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES,
                DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        builder.featuresToEnable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
        builder.modulesToInstall(JSR310Module.class);
        return builder.build();
    }
}
wilkinsona commented 9 years ago

Using @EnableWebMvc turns off Spring Boot's auto-configuration of Spring MVC, is that what you intended? Also, it's not clear what you mean by "it does not work".

If you'd like some help, please post on Stackoverflow using the spring-boot tag, describing the behaviour that you expected to see and what the actual behaviour was. A small sample that reproduces the problem would also help.

sdeleuze commented 9 years ago

Using @EnableWebMvc disable Spring Boot web autoconfiguration, so the ObjectMapper bean is not used anymore. In order to make it work, remove @EnableWebMvc. If you need to use Spring MVC JavaConfig, you can declare a WebMvcConfigurer bean instead, and return a WebMvcConfigurerAdapter instance.

hantsy commented 9 years ago

@sdeleuze I used custom WebMvcConfigurerAdpter class(WebConfig) which annotated withEnableWebMvc and @Configuration. And a standalone Jackson2ObjectMapperConfig for ObjectMapper(listed above). All of these are ported from my before projects(none spring boot project), but these did not work as exptected, the new Jdk 8 jsr310 jackson serialization configuration is not applied. As an end user of Spring boot, I hope my custom configuration owns high priority other than auto configuration provided by Spring Boot, thus I can move to Spring Boot painlessly.

hantsy commented 9 years ago

Thanks for your help, @sdeleuze , and @wilkinsona

sdeleuze commented 9 years ago

@hantsy As proposed by @wilkinsona , feel free to post a Stackoverflow question link, I will answer you there.