Spring настроить формат @ResponseBody JSON

Представьте, что у меня есть этот аннотированный метод в 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 с телом ответа
  2. Кто устанавливает тип содержимого ответа в Spring MVC (@ResponseBody)< /а>

Спасибо !


person Guido    schedule 27.01.2011    source источник


Ответы (11)


Для тех, кто использует настройку Spring на основе Java:

@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 из fastxml.

<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>
person Õzbek    schedule 13.02.2014

Мне нужно было решить очень похожую проблему, которая настраивает Jackson Mapper на «Не сериализовать нулевые значения ради Христа !!!».

Я не хотел оставлять причудливый тег mvc:annotation-driven, поэтому я нашел, как настроить ObjectMapper Джексона, не удаляя mvc:annotation-driven и добавляя не очень причудливый ContentNegotiatingViewResolver.

Прелесть в том, что вам не нужно писать код Java самостоятельно!

А вот конфигурация XML (не путайте с разными пространствами имен классов Джексона, я просто использовал новую библиотеку 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>
person tkuty    schedule 14.12.2012
comment
Вы также можете воспользоваться Jackson2ObjectMapperFactoryBean, как указано @Tody.Lu, и просто установить для свойства serializationInclusion значение NON_NULL - person keeshux; 28.02.2014

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://stackoverflow.com/questions/3661769 -->
<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);
person Guido    schedule 28.01.2011
comment
Это не работает для меня. Получил этот org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping:86-AbstractDetectingUrlHandlerMapping.java {http-thread-pool-4848-(2)} | Отклоненное имя компонента 'jacksonObjectMapper': URL-адреса не определены - person Bobo; 19.05.2011
comment
Просто с <mvc:annotation-driven /> это не сработает, я выяснил на собственном горьком опыте. - person JD Frias; 09.10.2012
comment
AnnotationMethodHandlerAdapter устарел... вместо этого используйте AbstractHttpMessageConverter - person Õzbek; 13.02.2014

В 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>
person Tody.Lu    schedule 03.05.2013
comment
Для меня, когда я проверяю messageConverters в классе AbstractMessageConverterMethodProcessor, он содержит два MappingJackson2HttpMessageConverter. Один через spring другой через нашу конфигурацию. - person sanjay patel; 26.05.2016
comment
Для меня, когда я проверяю messageConverters в классе AbstractMessageConverterMethodProcessor, он содержит два MappingJackson2HttpMessageConverter. Один через spring другой через нашу конфигурацию. - person Dipanshu Verma; 25.06.2017

Для Spring версии 4.1.3+

Я попробовал решение Jama, но затем все ответы были возвращены с типом контента «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(...)

person Melv    schedule 01.09.2015

Я написал свой собственный 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;
        }

}

И использование в весенней конфигурации:

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

person oehmiche    schedule 18.01.2012

Не отвечает на вопрос, но это лучший результат Google.

Если кто-то придет сюда и захочет сделать это для Spring 4 (как это случилось со мной), вы можете использовать аннотацию

@JsonInclude(Include.NON_NULL)

на возвращающемся классе.

person Mário Fernandes    schedule 20.01.2015
comment
Это решает включение ненулевых параметров. Но как насчет других настроек, таких как JsonGenerator.Feature.QUOTE_FIELD_NAMES или SerializationConfig.Feature.INDENT_OUTPUT? - person Guido; 30.01.2015
comment
QUOTE_FIELD_NAMES включен по умолчанию, поэтому spring 4 по крайней мере (не тестировалось с 3) делает это автоматически, это в javadoc: /** * (основная информация).... * Функция включена по умолчанию (поскольку это требуется по спецификации JSON). */ Что касается INDENT_OUTPUT, вы можете сделать: MyObject result = ...; Сопоставитель ObjectMapper = новый ObjectMapper(); return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(результат); - person Mário Fernandes; 02.02.2015

Вы можете настроить ObjectMapper как bean-компонент в XML-файле Spring. Ссылку на 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>

Где customObjectMapper определяется в другом месте файла xml. Обратите внимание, что вы можете напрямую устанавливать значения свойств Spring с помощью определений Enums Jackson; см. этот вопрос.

Кроме того, ContentNegotiatingViewResolver, вероятно, не требуется, это просто код, который я использую в существующем проекте.

person AngerClown    schedule 28.01.2011
comment
Спасибо, ваш ответ мне очень помог. Смотрите мой ответ с окончательной и рабочей конфигурацией. - person Guido; 28.01.2011

Взгляните на подход Рика Хайтауэра. Его подход позволяет избежать настройки ObjectMapper как синглтона и позволяет фильтровать ответ JSON для одного и того же объекта по-разному для каждого метода запроса.

http://www.jroller.com/RickHigh/entry/filtering_json_feeds_from_spring

person Greg Sheremeta    schedule 13.10.2011
comment
Интересный. Спасибо, Грег. - person Guido; 14.10.2011
comment
Для всех, кто использует это, при подключении совета с использованием XML убедитесь, что это делается в контексте сервлета, а не в корневом контексте. Совет не применяется к @Controllers, если XML находится в корневом контексте. - person Greg Sheremeta; 20.10.2011
comment
Сайт недоступен. - person Mateusz Stefek; 21.09.2019

Вы можете сделать следующее (версия Джексона ‹ 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);
    }
}

Весенняя конфигурация:

<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>
person I V    schedule 25.12.2013

Да, но что произойдет, если вы начнете использовать миксины, например, вы не сможете использовать ObjectMapper как синглтон, потому что вы будете применять конфигурацию глобально. Итак, вы будете добавлять или настраивать классы примесей в одном и том же экземпляре ObjectMapper?

person serge    schedule 09.02.2011
comment
Хороший вопрос. Я не знаю, возможно ли это с моим подходом. - person Guido; 10.02.2011