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
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);
Ответ 2
Для пользователей, которые используют конфигурацию 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
- который находится из quickxml.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.1</version>
</dependency>
Если вы хотите использовать maphaus-jackson mapper, вместо этого используйте MappingJacksonHttpMessageConverter
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${codehaus.jackson.version}</version>
</dependency>
Ответ 3
Мне нужно решить очень похожую проблему, которая настраивает Jackson Mapper на "Не сериализуйте нулевые значения ради Христа!".
Я не хотел оставлять причудливый mvc: аннотация, управляемый тегом, поэтому я нашел, как настроить Jackson ObjectMapper без удаления mvc: аннотирование и добавление не очень интересного ContentNegotiatingViewResolver.
Прекрасно то, что вам не нужно писать код 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>
Ответ 4
В 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>
Ответ 5
Для 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(...)
Ответ 6
Я написал свой собственный 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>
Ответ 7
Не отвечает на вопрос, но это лучший результат google.
Если кто-то приходит сюда и хочет сделать это для Spring 4 (как это случилось со мной), вы можете использовать аннотацию
@JsonInclude(Include.NON_NULL)
в возвращаемом классе.
Ответ 8
Взгляните на подход Рика Хайтауэра. Его подход избегает настройки ObjectMapper в качестве одноэлементного и позволяет отфильтровать ответ JSON для одного и того же объекта по-разному для каждого метода запроса.
http://www.jroller.com/RickHigh/entry/filtering_json_feeds_from_spring
Ответ 9
Вы можете настроить ObjectMapper как bean в файле Spring xml. То, что содержит ссылку на 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; см. этот вопрос.
Кроме того, ContentNegotiationViewResolver, вероятно, не требуется, это просто код, который я использую в существующем проекте.
Ответ 10
Да, но что произойдет, если вы начнете использовать mixins, например, вы не можете иметь ObjectMapper в качестве одноэлементного, потому что вы будете применять конфигурацию по всему миру. Итак, вы будете добавлять или устанавливать классы mixin в том же экземпляре ObjectMapper?
Ответ 11
Вы можете сделать следующее (версия 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 config:
<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>