Spring MVC 3.2 and JSON ObjectMapper issue

后端 未结 2 1389
执念已碎
执念已碎 2020-12-28 21:03

I have recently upgraded my Spring version to 3.2.0 from 3.1.2. I find that that JSON properties like wrap root element, prevent null values that are defined in ObjectMapper

相关标签:
2条回答
  • 2020-12-28 21:30

    Even though this one is an old post, I phased a similar (maybe even the same?) problem. I tracked it down to the following issue (and this might help others):

    • An extra library that was added has a transient dependency on Jackson 2.x
    • So far we had a dependency on Jackson 1.x
    • Between Jackson 1.x and Jackson 2.x the namespace was changed to avoid conflicts
    • Sprint started to pick up the newer (2.x) version
    • The annotation was still to the old (1.x) version

    To solve the issue I can either:

    1. remove the 2.x version again
    2. Upgrade the annotation to the 2.x version
    3. Add an extra annotation to also cover the 2.x version

    In my case I went for solution 3, since we require the two different versions and the additional annotation didn't add a big overhead.

    0 讨论(0)
  • 2020-12-28 21:33

    Disclaimer: I could not determine why the code in question is not working, but I could reproduce the problem. This answer does provide an alternate approach that works for me.

    It could be a bug, as I can reproduce the problem with both with explicit config:

    <bean id="jacksonObjectMapper" class="com.demo.CustomJacksonObjectMapper"/>
    
    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
       <property name="objectMapper" ref="jacksonObjectMapper"/>
       <property name="supportedMediaTypes" value="application/json"/>
    </bean>
    

    and via the mvc:message-converter:

    <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper" ref="jacksonObjectMapper" />
            </bean>
       </mvc:message-converters>
    </mvc:annotation-driven>
    

    where both give me {"foo":null,"bar":"bar"} when using the example class

    Demo.java

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    import org.codehaus.jackson.annotate.JsonProperty;
    
    @Controller
    @RequestMapping("/data")
    public class Data {
        @RequestMapping
        @ResponseBody
        public Dummy dataIndex() {
            return new Dummy();
        }
    
        public class Dummy {
            String foo = null;
            @JsonProperty
            public String foo() {
                return foo;
            }
            String bar = "bar";
            @JsonProperty
            public String bar() {
                return bar;
            }
        }
    }
    

    However, I would have thought the mvc:message-converter method would work, only because I have seen issues overriding the default bean registration that <mvc:annotation-driven/> performs (see Web MVC Framework) and using the nested configuration is preferred(?).

    Maybe the Jackson 2 support has caused some backwards compatibility issues with Jackson 1?

    However, switching to the MappingJackson2HttpMessageConverter (supported in Spring 3.1.2 and Spring 3.2), I am able to alter the ObjectMapper configuration to not write null values and wrap the JSON output... but only when using the config inside the <mvc:message-converters/>!

    I get {"Dummy":{"bar":"bar"}} with the following changes:

    pom.xml

    <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-core</artifactId>
       <version>2.1.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.1.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.1.0</version>
    </dependency>
    

    CustomJacksonObjectMapper.java

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import static com.fasterxml.jackson.annotation.JsonInclude.*;
    
    public class CustomJacksonObjectMapper extends ObjectMapper {
    
    public CustomJacksonObjectMapper() {
        super();
        this.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
        this.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
        this.setSerializationInclusion(Include.NON_NULL);
       }
    }
    

    Demo.java switch to new package structure for Jackson 2

    import com.fasterxml.jackson.annotation.JsonProperty;
    

    demo-servlet.xml

    <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper" ref="jacksonObjectMapper" />
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    

    Lastly, according to the SerializationConfig.Feature documentation, WRITE_NULL_PROPERTIES feature is deprecated < v2.0 and you should be using SerializationConfig.setSerializationInclusion() anyway. I assume this is why the @SuppressWarnings("deprecation") exists in your code. In Jackson >= v2.0, it has been removed, hence the code change in the CustomJacksonObjectMapper.java example.

    Configuring ObjectMapper in Spring proposes an alternate solution.

    Hope it helps!

    0 讨论(0)
提交回复
热议问题