web services - restful - Spring, Jackson y personalización(por ejemplo CustomDeserializer)
spring rest post json example (6)
En mi caso (Spring 3.2.4 y Jackson 2.3.1), configuración XML para el serializador personalizado:
<mvc:annotation-driven>
<mvc:message-converters register-defaults="false">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="serializers">
<array>
<bean class="com.example.business.serializer.json.CustomObjectSerializer"/>
</array>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
fue de forma inexplicable sobrescrito de nuevo al valor predeterminado por algo.
Esto funcionó para mí:
CustomObject.java
@JsonSerialize(using = CustomObjectSerializer.class)
public class CustomObject {
private Long value;
public Long getValue() {
return value;
}
public void setValue(Long value) {
this.value = value;
}
}
CustomObjectSerializer.java
public class CustomObjectSerializer extends JsonSerializer<CustomObject> {
@Override
public void serialize(CustomObject value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("y", value.getValue());
jgen.writeEndObject();
}
@Override
public Class<CustomObject> handledType() {
return CustomObject.class;
}
}
No se necesita ninguna configuración XML ( <mvc:message-converters>(...)</mvc:message-converters>
) en mi solución.
Siendo aún un poco desconocido con Spring, he encontrado un problem que hace que sea necesario implementar un deserializador personalizado para Jackson. El procedimiento se describe en un pequeño tutorial , sin embargo, estoy atascado con Spring. No entiendo, donde
ObjectMapper mapper = new ObjectMapper();
en Spring MVC se lleva a cabo cuando json se deserializa mediante un método de una clase de controlador. Así que no sé qué hacer para reemplazar el deserializador predeterminado por un deserializador personalizado.
Cualquier sugerencia más bienvenida.
La solución a la que hace referencia Rakesh probablemente funcione con Spring MVC 3.0 pero con 3.1 parte de la infraestructura de MVC ha cambiado . Como resultado, es posible que no tenga un Bean AnnotationMethodHandlerAdapter
registrado en el contexto de su aplicación y terminará con una BeanCreationException
en el momento de la inicialización.
Para Spring MVC 3.1, el elemento mvc:annotation-driven
creará RequestMappingHandlerAdapter para ti, por lo que deberías autocablear ese tipo. Todavía proporcionará acceso a la lista de HttpMessageConverters registrados y le permitirá establecer la propiedad ObjectMapper en MappingJacksonHttpMessageConverter
. Esto también requiere un ligero cambio dentro del init
. método al tipo de la referencia HttpMessageConverters.
La clase actualizada se ve así:
@Component
public class JacksonFix {
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
private CustomObjectMapper objectMapper;
@PostConstruct
public void init() {
List<HttpMessageConverter<?>> messageConverters = requestMappingHandlerAdapter.getMessageConverters();
for (HttpMessageConverter<?> messageConverter : messageConverters) {
if (messageConverter instanceof MappingJacksonHttpMessageConverter) {
MappingJacksonHttpMessageConverter m = (MappingJacksonHttpMessageConverter) messageConverter;
m.setObjectMapper(objectMapper);
}
}
}
// this will exist due to the <mvc:annotation-driven/> bean
@Autowired
public void setRequestMappingHandlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
}
@Autowired
public void setObjectMapper(CustomObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
}
ACTUALIZACIÓN : Resulta que lo más fácil de hacer con Spring 3.1 es agregar alguna configuración adicional a su configuración de MVC:
<mvc:annotation-driven conversion-service="applicationConversionService">
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="customObjectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Eso agregará una nueva instancia de MappingJacksonHttpMessageConverter
con el ObjectMapper personalizado antes de cualquiera de los HttpMessageConverters predeterminados (que todavía están presentes debido a register-defaults="true"
).
Los documentos de primavera para el estado MappingJacksonHttpMessageConverter:
2.4.5 MappingJacksonHttpMessageConverter
Una implementación HttpMessageConverter que puede leer y escribir JSON utilizando ObjectMapper del procesador Jackson JSON. El mapeo JSON se puede personalizar según sea necesario mediante el uso de las anotaciones provistas por Jackson. Cuando se necesita más control, se puede inyectar un ObjectMapper personalizado a través de la propiedad ObjectMapper para los casos en que se deben proporcionar serializadores / deserializadores JSON personalizados para tipos específicos. Por defecto, este convertidor es compatible (aplicación / json).
¿No podría simplemente autorizar el acceso al ObjectMapper para modificar su comportamiento?
No dices cómo estás usando Jackson en Spring, así que supongo que lo estás usando a través de <mvc:annotation-driven/>
y @RequestBody
y / o @ResponseBody
.
Una de las cosas que hace <mvc:annotation-driven/>
es registrar un bean AnnotationMethodHandlerAdapter
que viene con un número de beans HttpMessageConverter
preconfigurados, incluido MappingJacksonHttpMessageConverter
, que maneja las MappingJacksonHttpMessageConverter
clases de modelo con anotaciones de Jackson.
Ahora MappingJacksonHttpMessageConverter
tiene un método setObjectMapper()
, que le permite sobrescribir el ObjectMapper
predeterminado. Pero como MappingJacksonHttpMessageConverter
se crea detrás de escena por <mvc:annotation-driven/>
, no se puede acceder a él.
Sin embargo, <mvc:annotation-driven/>
es solo un atajo conveniente. Es tan válido como declarar su propio bean AnnotationMethodHandlerAdapter
, inyectando en él su propio bean MappingJacksonHttpMessageConverter
(a través de la propiedad messageConverters
) e inyectando su propio ObjectMapper
personalizado en eso.
A continuación, tiene el problema de cómo crear un ObjectMapper
personalizado, ya que no es una clase muy amistosa con Spring. Sugiero escribir su propia implementación simple de FactoryBean
.
Entonces terminarías con algo como esto:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<bean class="com.x.MyObjectMapperFactoryBean"/>
</property>
</bean>
</property>
</bean>
Nueva forma de hacerlo en primavera 3.1:
http://magicmonster.com/kb/prg/java/spring/webmvc/mvc_spring_config_namespace.html
http://blog.springsource.org/2011/02/21/spring-3-1-m1-mvc-namespace-enhancements-and-configuration/
Te permite hacer algo como esto:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="customObjectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Ojalá supiera Spring MVC mejor, pero con implementaciones de Jax-RS como Jersey y RESTeasy, uno registra proveedores. Quizás Spring hace algo similar?