问题 Spring MVC 3.2和JSON ObjectMapper问题


我最近将我的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>

可能是什么问题?任何指针都表示赞赏。


6642
2018-01-15 08:28


起源

你确定吗? CustomJacksonObjectMapper用来? - axtavt


答案:


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

可以 是一个错误,因为我可以使用显式配置重现问题:

<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:消息转换器

<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:消息转换器 方法会工作,只是因为我已经看到覆盖默认bean注册的问题 <mvc:annotation-driven/> 执行(见 Web MVC框架并且首选使用嵌套配置(?)。

也许吧 杰克逊2支持 与Jackson 1造成了一些向后兼容问题?

但是,切换到 MappingJackson2HttpMessageConverter (在Spring 3.1.2和Spring 3.2中支持),I 上午 能够改变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;

演示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 功能已弃用<v2.0,您应该使用 SerializationConfig.setSerializationInclusion() 无论如何。我认为这就是为什么 @SuppressWarnings("deprecation") 存在于您的代码中。在Jackson> = v2.0中,它已被删除,因此代码更改了 CustomJacksonObjectMapper.java 例。

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

希望能帮助到你!


14
2018-01-15 11:50



非常感谢你花时间在短时间内回复。这对我有用。还要特别感谢stackoverflow.com提供这个分享这些东西的精彩平台,以及我可以用我的方式贡献的时间.... - spal
很乐意提供帮助:-)如果有人能够确定为什么杰克逊1被打破了,我会非常有兴趣知道为什么!如果我有时间,我会尝试更多地调试Spring。 - andyb
@andyb我试过这个解决方案,但是当我尝试启动Tomcat时,我得到'java.lang.ClassNotFoundException:com.demo.CustomJacksonObjectMapper'异常。你有什么主意吗?非常感谢! - Pmt
听起来像 CustomJacksonObjectMapper.java 示例类不在 com.demo 包。我没有这个src但是如果你还有问题我可以尝试重新创建它。 - andyb


即使这是一个旧帖子,我也会分阶段进行类似(甚至可能是相同的)问题。我将其跟踪到以下问题(这可能有助于其他人):

  • 添加的额外库对Jackson 2.x具有短暂依赖性
  • 到目前为止,我们依赖于杰克逊1.x.
  • 在Jackson 1.x和Jackson 2.x之间,命名空间被更改以避免冲突
  • Sprint开始采用更新的(2.x)版本
  • 注释仍然是旧的(1.x)版本

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

  1. 再次删除2.x版本
  2. 将注释升级到2.x版本
  3. 添加额外的注释也可以覆盖2.x版本

在我的情况下,我去了解决方案3,因为我们需要两个不同的版本,并且额外的注释没有增加很大的开销。


0
2018-02-10 10:01