Spring MVC 3.2 and JSON ObjectMapper issue



我最近将Spring版本从3.1.2升级到3.2.0。我发现JSON属性,如包装根元素,防止在ObjectMapper中定义的空值不再工作。

下面是代码片段
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" /> 
    <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="true" />
    <property name="ignoreAcceptHeader" value="false" /> 
    <property name="mediaTypes" >
        <value>
            json=application/json
            xml=application/xml
        </value>
    </property>
</bean>

和JSON转换器

<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
   <property name="objectMapper" ref="customJacksonObjectMapper"/>  
   <property name="supportedMediaTypes" value="application/json"/>
</bean>

对象映射代码

public class CustomJacksonObjectMapper extends ObjectMapper {
@SuppressWarnings("deprecation")
public CustomJacksonObjectMapper() {
    super();
    final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
    this.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.UNWRAP_ROOT_VALUE, true);
    this.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
    this.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRITE_NULL_PROPERTIES, false);
    this.setDeserializationConfig(this.getDeserializationConfig().withAnnotationIntrospector(introspector));
    this.setSerializationConfig(this.getSerializationConfig().withAnnotationIntrospector(introspector));
   }
}
杰克逊版

<dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-xc</artifactId>
        <version>1.9.7</version>
    </dependency>

有什么问题吗?

免责声明:我无法确定为什么有问题的代码不能工作,但我可以重现问题。这个答案确实为我提供了另一种方法。

可能是一个bug,因为我可以用显式配置重现这个问题:

<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>

和通过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>

当使用示例类

时,两者都给我{"foo":null,"bar":"bar"}

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;
        }
    }
}

然而,我本以为mvc:message-converter方法会起作用,只是因为我看到了覆盖<mvc:annotation-driven/>执行的默认bean注册的问题(参见Web mvc框架),并且首选使用嵌套配置(?)。

也许Jackson 2的支持导致了Jackson 1的向后兼容性问题?

然而,切换到MappingJackson2HttpMessageConverter(在Spring 3.1.2和Spring 3.2中支持),我am能够更改ObjectMapper配置以不写空值并包装JSON输出…仅当使用<mvc:message-converters/>内部的配置时!

我得到{"Dummy":{"bar":"bar"}}与以下变化:

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切换到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>

最后,根据SerializationConfig.Feature文档,WRITE_NULL_PROPERTIES特性已弃用<无论如何,您应该使用SerializationConfig.setSerializationInclusion()。我想这就是为什么@SuppressWarnings("deprecation")存在于您的代码中。在Jackson>= v2.0中,它已被删除,因此在CustomJacksonObjectMapper.java示例中的代码更改。

在Spring中配置ObjectMapper提出了另一个解决方案。

希望有帮助!

虽然这是一个老帖子,但我发现了一个类似的(甚至可能是相同的?)问题。我将其追踪到以下问题(这可能对其他人有所帮助):

  • 添加的额外库对Jackson 2.x有暂时依赖
  • 到目前为止我们还依赖于Jackson 1.x
  • Between Jacksonx和杰克逊2。
  • Sprint开始使用较新的(2.x)版本
  • 注释仍然是旧的(1.x)版本

要解决这个问题,我可以:

  1. 删除2。x版本再次出现
  2. 将注释升级到2。x版本
  3. 添加一个额外的注释,也覆盖2。x版本

在我的例子中,我选择了解决方案3,因为我们需要两个不同的版本,并且额外的注释不会增加很大的开销。

最新更新