2010-12-14 17 views
37

Estoy usando Spring MVC para manejar las solicitudes de JSON POST. Debajo de las portadas, estoy usando el MappingJacksonHttpMessageConverter creado en el procesador Jackson JSON y habilitado cuando usa el mvc: anotado por la anotación.RESTO con enlace de datos completo de Spring y Jackson

Una de mis servicios recibe una lista de acciones:

@RequestMapping(value="/executeActions", method=RequestMethod.POST) 
    public @ResponseBody String executeActions(@RequestBody List<ActionImpl> actions) { 
     logger.info("executeActions"); 
     return "ACK"; 
    } 

he encontrado que Jackson mapea la requestBody a una lista de elementos java.util.LinkedHashMap (enlace de datos simples). En cambio, me gustaría que la solicitud se vincule a una Lista de objetos tipeados (en este caso, "ActionImpl").

Sé que esto es fácil de hacer si se utiliza ObjectMapper de Jackson directamente:

List<ActionImpl> result = mapper.readValue(src, new TypeReference<List<ActionImpl>>() { }); 

pero me preguntaba cuál es la mejor manera de lograr esto al utilizar Spring MVC y MappingJacksonHttpMessageConverter. ¿Algún consejo?

Gracias

Respuesta

27

Sospecho que el problema se debe al borrado de tipo, es decir, en lugar de pasar el tipo de parámetro genérico, tal vez solo se pase actions.getClass(); y esto daría tipo equivalente a la lista <?>.

Si esto es cierto, una posibilidad sería utilizar una sub-clase intermedia, como:

public class ActionImplList extends ArrayList<ActionImpl> { } 

porque esto la retención de la información de tipo, incluso si se pasa única clase. Entonces:

public @ResponseBody String executeActions(@RequestBody ActionImplList actions) 

que hacer el truco. No es óptimo, pero debería funcionar.

Espero que alguien con más conocimientos de Spring MVC pueda arrojar luz sobre por qué no se pasa el tipo de parámetro (¿quizás es un error?), Pero al menos hay un problema.

+1

Tienes razón y el truco que propones funciona. Muchas gracias –

+2

Este problema ocurrió en Spring 3.1 pero se corrigió en Spring 3.2: https://jira.spring.io/browse/SPR-9570 – Martin

0

¿Usted ha intentado declarar el método como:

executeActions(@RequestBody TypeReference<List<ActionImpl>> actions) 

Yo no lo he probado, pero en base a su pregunta es la primera cosa que me gustaría tratar.

+0

Buen punto, pero no funciona. Jackson intenta deserializar la Lista recibida como una instancia de TypeReference y esto produce una JsonMappingException. –

2

Esta pregunta ya es antigua, pero creo que puedo contribuir un poco de todos modos.

Al igual que StaxMan señaló, esto se debe a borrado de tipo. Definitivamente debería ser posible, porque puede obtener los argumentos genéricos a través de la reflexión de la definición del método. Sin embargo, el problema es la API de la HttpMessageConverter:

T read(Class<? extends T> clazz, HttpInputMessage inputMessage); 

Aquí, sólo se List.class se pasa al método. Entonces, como puede ver, es imposible implementar un HttpMessageConverter que calcule el tipo real mirando el tipo de parámetro de método, ya que no está disponible.

Sin embargo, es posible codificar su propia solución: simplemente no usará HttpMessageConverter. Spring MVC le permite escribir su propio WebArgumentResolver que se inicia antes de los métodos de resolución estándar.Por ejemplo, puede usar su propia anotación personalizada (@JsonRequestBody?) Que usa directamente un ObjectMapper para analizar su valor. Va a ser capaz de proporcionar el tipo de parámetro del método:

final Type parameterType= method.getParameterTypes()[index]; 
List<ActionImpl> result = mapper.readValue(src, new TypeReference<Object>>() { 
    @Override 
    public Type getType() { 
     return parameterType; 
    } 
}); 

No es realmente la forma typereference estaba destinado a ser utilizado supongo, pero ObjectMapper no proporciona un método más adecuado.

41

He encontrado que también puede evitar el problema del borrado de tipo utilizando una matriz como @RequestBody en lugar de una colección. Por ejemplo, la siguiente funcionaría:

public @ResponseBody String executeActions(@RequestBody ActionImpl[] actions) { //... } 
+0

¡Esta es la solución más limpia de las presentadas aquí! – metadaddy

+1

Estaba atascado en un problema similar, intenté esto y funciona. Sin embargo, al actualizar a 3.1.1.Liberar incluso la lista funcionaría ... – sunny

+0

El comportamiento es arbitrario ... todavía confuso – sunny

8

Para su información, la función estará disponible en la primavera de 3,2 (ver https://jira.springsource.org/browse/SPR-9570)

Acabo de probar que la corriente M2 y funciona como un encanto de el cuadro (no es necesario proporcionar una anotación adicional para proporcionar el tipo parametrizado, se resolverá automáticamente con el nuevo MessageConverter)

Cuestiones relacionadas