java - jsonview - jackson spring mvc
Spring configure @ResponseBody formato JSON (11)
Imagina que tengo este método anotado en un Spring 3 @Controller
@RequestMapping("")
public @ResponseBody MyObject index(@RequestBody OtherObject obj) {
MyObject result = ...;
return result;
}
Pero necesito configurar el formato json de salida, como si lo estuviera haciendo:
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);
¿Hay alguna forma de configurar este comportamiento?
He encontrado un par de preguntas relacionadas, pero no estoy seguro de cómo adaptarlas a mi caso específico:
- prefixjson de primavera con responsebody
- Quién establece el tipo de contenido de respuesta en Spring MVC (@ResponseBody)
Gracias !
AngerClown me indicó la dirección correcta.
Esto es lo que finalmente hice, solo en caso de que alguien lo encuentre útil.
<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://.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>
Todavía tengo que encontrar la forma de configurar las otras propiedades tales como:
om.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true);
Eche un vistazo al enfoque de Rick Hightower. Su enfoque evita configurar ObjectMapper como singleton y le permite filtrar la respuesta JSON para el mismo objeto de diferentes maneras para cada método de solicitud.
http://www.jroller.com/RickHigh/entry/filtering_json_feeds_from_spring
En primavera3.2, la nueva solución es presentada por: http://static.springsource.org/spring/docs/3.2.0.BUILD-SNAPSHOT/api/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.html , el a continuación está mi ejemplo:
<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>
Escribí mi propia FactoryBean que crea una instancia de ObjectMapper (versión simplificada):
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;
}
}
Y el uso en la configuración de primavera:
<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>
Necesité resolver un problema muy similar, que configura a Jackson Mapper para "¡No serializar valores nulos por el amor de Cristo!".
No quería dejar el elegante mvc: etiqueta impulsada por anotaciones, así que descubrí cómo configurar el ObjectMapper de Jackson sin eliminar mvc: impulsado por anotaciones y agregando no realmente lujoso ContentNegotiatingViewResolver.
¡Lo hermoso es que no tienes que escribir ningún código Java tú mismo!
Y aquí está la configuración XML (no se confunda con diferentes espacios de nombres de las clases de Jackson, simplemente utilicé la nueva biblioteca Jakson 2.x ... lo mismo debería funcionar también con las bibliotecas de 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>
No responde la pregunta, pero este es el resultado superior de google.
Si alguien viene aquí y quiere hacerlo para la primavera 4 (como me sucedió a mí), puedes usar la anotación
@JsonInclude(Include.NON_NULL)
en la clase que regresa
Para las personas que usan la configuración Spring basada en 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);
}
....
}
Estoy usando MappingJackson2HttpMessageConverter
, que es de fasterxml.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.1</version>
</dependency>
Si quieres usar el mapeador codehaus-jackson, utiliza este MappingJacksonHttpMessageConverter
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${codehaus.jackson.version}</version>
</dependency>
Puede configurar ObjectMapper como un bean en su archivo Spring xml. Lo que contiene una referencia a ObjectMapper es la clase MappingJacksonJsonView
. Luego debe adjuntar la vista a ViewResolver.
Algo como esto debería funcionar:
<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>
Donde customObjectMapper
está definido en otra parte del archivo xml. Tenga en cuenta que puede establecer directamente los valores de propiedad de Spring con los Enums que Jackson define; mira esta pregunta
Además, ContentNegotiatingViewResolver probablemente no sea necesario, es solo el código que estoy usando en un proyecto existente.
Puedes hacer lo siguiente (versión de jackson <2):
Clase de mapeador personalizado:
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);
}
}
Configuración de primavera:
<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>
Sí, pero qué sucede si comienza a usar mixins, por ejemplo, no puede tener ObjectMapper como singleton porque aplicará la configuración globalmente. ¿Entonces agregará o establecerá las clases mixin en la misma instancia de ObjectMapper?
Para Spring versión 4.1.3+
Intenté con la solución de Jama, pero luego todas las respuestas fueron devueltas con Content-type ''application / json'', incluida la principal página HTML generada.
Reemplazar configureMessageConverters(...)
evita que el muelle configure los conversores predeterminados. Spring 4.1.3 permite la modificación de conversores ya configurados anulando 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);
}
}
ver
org.springframework..WebMvcConfigurationSupport#getMessageConverters()
ver
org.springframework..WebMvcConfigurationSupport#addDefaultHttpMessageConverters(...)