Spring配置@ResponseBody JSON格式

40

假设我在一个Spring 3的@Controller中有这样一个带注释的方法

@RequestMapping("")
public @ResponseBody MyObject index(@RequestBody OtherObject obj) {
    MyObject result = ...;
    return result;
}

但我需要配置输出的 JSON 格式,就像我这样做一样:

ObjectMapper om = new ObjectMapper();
om.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true);
om.getSerializationConfig()
        .setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
om.getSerializationConfig()
        .set(SerializationConfig.Feature.INDENT_OUTPUT, false);
有没有办法配置这种行为?
我找到了一些相关的问题,但是我不确定如何将它们适应我的具体情况:
  1. spring prefixjson with responsebody
  2. Who sets response content-type in Spring MVC (@ResponseBody)
谢谢!
11个回答

31

对于使用基于Java的Spring配置的人:

@Configuration
@ComponentScan(basePackages = "com.domain.sample")
@EnableWebMvc
public class SpringConfig extends WebMvcConfigurerAdapter {
....

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        converter.setObjectMapper(objectMapper);
        converters.add(converter);
        super.configureMessageConverters(converters);
    }

....

}

我正在使用MappingJackson2HttpMessageConverter,它来自 fasterxml。

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

如果你想使用codehaus-jackson映射器,那么请使用这个 MappingJacksonHttpMessageConverter

 <dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>${codehaus.jackson.version}</version>
 </dependency>

28

我需要解决一个非常类似的问题,即配置Jackson Mapper以"不要为了上帝的缘故序列化空值!!!"。

我不想放弃花哨的mvc:annotation-driven标签,所以我找到了如何在不删除mvc:annotation-driven并添加不是很花哨的ContentNegotiatingViewResolver的情况下配置Jackson的ObjectMapper。

美妙的事情是你不必自己编写任何Java代码!

这里是XML配置(不要被Jackson类的不同命名空间所困扰,我只是使用了新的Jakson 2.x库...使用Jackson 1.x库也应该是一样的):

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                    <property name="serializationInclusion">
                        <value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
                    </property>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

1
你也可以像@Tody.Lu所指出的那样利用Jackson2ObjectMapperFactoryBean,并将serializationInclusion属性设置为NON_NULL - keeshux

15

AngerClown 指引了我正确的方向。

这就是我最终做的,以防有人觉得有用。

<bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper" ref="jacksonObjectMapper" />
            </bean>
        </list>
    </property>
</bean>

<!-- jackson configuration : https://dev59.com/31DTa4cB1Zd3GeqPIFBh -->
<bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
    factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" />
<bean
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="jacksonSerializationConfig" />
    <property name="targetMethod" value="setSerializationInclusion" />
    <property name="arguments">
        <list>
            <value type="org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion">NON_DEFAULT</value>
        </list>
    </property>
</bean>

我还需要弄清楚如何配置其他属性,例如:

om.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true);

这对我不起作用。得到了这个错误信息:“org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping:86-AbstractDetectingUrlHandlerMapping.java {http-thread-pool-4848-(2)} | 拒绝的bean名称'jacksonObjectMapper':未识别出URL路径”。 - Bobo
2
请注意,这将无法与<mvc:annotation-driven />一起使用,我已经吃过亏了。 - JD Frias
1
AnnotationMethodHandlerAdapter已经被弃用,请使用AbstractHttpMessageConverter代替。 - Jama A.

10

在Spring3.2中,引入了新的解决方案: http://static.springsource.org/spring/docs/3.2.0.BUILD-SNAPSHOT/api/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.html ,下面是我的示例:

 <mvc:annotation-driven>
   ​<mvc:message-converters>
     ​​<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
       ​​​<property name="objectMapper">
         ​​​​<bean
 class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
           ​​​​​<property name="featuresToEnable">
             ​​​​​​<array>
               ​​​​​​​<util:constant static-field="com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES" />
             ​​​​​​</array>
           ​​​​​</property>
         ​​​​</bean>
       ​​​</property>
     ​​</bean>
   ​</mvc:message-converters>
 </mvc:annotation-driven>

当我在AbstractMessageConverterMethodProcessor类中检查messageConverters时,它包含两个MappingJackson2HttpMessageConverter。一个是通过Spring配置的,另一个是通过我们自己的配置的。 - sanjay patel
当我在AbstractMessageConverterMethodProcessor类中检查messageConverters时,它包含两个MappingJackson2HttpMessageConverter。一个是通过Spring配置的,另一个是通过我们自己的配置的。 - Dipanshu Verma

10

对于Spring版本4.1.3+

我尝试了Jama的解决方案,但是所有响应都返回Content-type为"application/json",包括主生成的HTML页面。

通过覆盖configureMessageConverters(...)方法可以阻止Spring设置默认转换器。Spring 4.1.3允许通过覆盖extendMessageConverters(...)来修改已配置的转换器:

@Configuration
public class ConverterConfig extends WebMvcConfigurerAdapter {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof AbstractJackson2HttpMessageConverter) {
                AbstractJackson2HttpMessageConverter c = (AbstractJackson2HttpMessageConverter) converter;
                ObjectMapper objectMapper = c.getObjectMapper();
                objectMapper.setSerializationInclusion(Include.NON_NULL);
            }
        }

        super.extendMessageConverters(converters);
    }
}

请参考org.springframework..WebMvcConfigurationSupport#getMessageConverters()

请参考org.springframework..WebMvcConfigurationSupport#addDefaultHttpMessageConverters(...)


4

这并没有回答问题,但这是谷歌搜索的最佳结果。

如果有人来到这里想要为Spring 4做这件事(就像我一样),您可以使用注解。

@JsonInclude(Include.NON_NULL)

在返回类上。

这解决了非空参数的包含问题。但是其他自定义设置如JsonGenerator.Feature.QUOTE_FIELD_NAMES或SerializationConfig.Feature.INDENT_OUTPUT呢? - Guido
QUOTE_FIELD_NAMES默认情况下已启用,因此至少在Spring 4中(未测试3),它会自动执行,这在javadoc中有说明: /** *(主要信息).... *该功能默认已启用(因为它是JSON规范所需的)。 */ 至于INDENT_OUTPUT,您可以执行以下操作: MyObject result = ...; ObjectMapper mapper = new ObjectMapper(); return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result); - Mário Fernandes

4
我编写了自己的FactoryBean,用于实例化ObjectMapper(简化版本):
 public class ObjectMapperFactoryBean implements FactoryBean<ObjectMapper>{

        @Override
        public ObjectMapper getObject() throws Exception {
                ObjectMapper mapper = new ObjectMapper();
                mapper.getSerializationConfig().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
                return mapper;
        }

        @Override
        public Class<?> getObjectType() {
                return ObjectMapper.class;
        }

        @Override
        public boolean isSingleton() {
                return true;
        }

}

在Spring配置中的使用:

<bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper" ref="jacksonObjectMapper" />
            </bean>
        </list>
    </property>
</bean>


2
您可以在Spring xml文件中将ObjectMapper配置为bean。保存ObjectMapper引用的是MappingJacksonJsonView类。然后,您需要将视图附加到ViewResolver上。
类似以下内容应该有效:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
      <property name="mediaTypes">
      <map>
        <entry key="json" value="application/json" />
        <entry key="html" value="text/html" />
      </map>
    </property>
    <property name="viewResolvers">
      <list>
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <property name="prefix" value="/WEB-INF/jsp/" />
          <property name="suffix" value=".jsp" />
        </bean>
      </list>
    </property>
    <property name="defaultViews">
      <list>
        <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
          <property name="prefixJson" value="false" />
          <property name="objectMapper" value="customObjectMapper" />
        </bean>
      </list>
    </property>
  </bean>

在xml文件的其他位置定义了customObjectMapper。请注意,您可以直接使用Jackson定义的枚举设置Spring属性值;请参见此问题

另外,ContentNegotiatingViewResolver可能不是必需的,这只是我在现有项目中使用的代码。


1
谢谢,你的回答帮了我很多。请查看我的回答,其中包括最终和可工作的配置。 - Guido

2
你可以按照以下步骤进行操作(jackson版本小于2):
自定义映射器类:
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;

public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
        super.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true);
        super.getSerializationConfig()
                .setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
        super.getSerializationConfig()
                .set(SerializationConfig.Feature.INDENT_OUTPUT, false);
    }
}

Spring配置:

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="false">
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
            <property name="objectMapper">
                <bean class="package.CustomObjectMapper"/>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

2

有趣。谢谢,Greg。 - Guido
1
对于其他使用者,如果使用XML进行连接,请确保在servlet上下文中完成,而不是根上下文中完成。如果XML位于根上下文中,则该建议不会应用于@Controllers。 - Greg Sheremeta
网站已经崩溃。 - Mateusz Stefek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接