No se puede deserializar la instancia de java.util.ArrayList fuera de VALUE_STRING

Tengo un servicio REST construido con Jersey y desplegado en App Engine. El servicio REST implementa el verbo PUT que consume un tipo de aplicación / json. El enlace de datos es realizado por Jackson.

El verbo consume una relación de departamentos de empresa representada en JSON como

{"name":"myEnterprise", "departments":["HR","IT","SC"]} 

En el lado del cliente, uso gson para convertir la representación JSON en un objeto java. Luego, paso el objeto a mi servicio REST y funciona bien.

Problema:

Cuando mi representación JSON tiene solo un elemento en la colección

 {"name":"myEnterprise", "departments":["HR"]} 

el servicio no puede deserializar el objeto.

 ATTENTION: /enterprise/enterprise: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token at [Source: org.mortbay.jetty.HttpParser$Input@5a9c5842; line: 1, column: 2 

Según informaron otros usuarios, la solución es agregar el indicador ACCEPT_SINGLE_VALUE_AS_ARRAY (por ejemplo, Jersey: no se puede deserializar la instancia de ArrayList fuera de String ). Sin embargo, no estoy controlando un ObjectMapper porque, en el lado del servicio, Jackson lo hace de forma transparente.

Pregunta:

¿Hay alguna forma de configurar ObjectMapper en el lado del servicio para habilitar ACCEPT_SINGLE_VALUE_AS_ARRAY? anotaciones? web.xml?

Detalles del código

Objeto Java:

 @XmlRootElement public class Enterprise { private String name; private List departments; public Enterprise() {} public String getName() { return name; } public void setName(String name) { this.name = name; } public List getDepartments() { return departments; } public void setDepartments(List departments) { this.departments = departments; } } 

El lado del servicio REST:

  @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/enterprise") public Response putEnterprise(Enterprise enterprise, @Context HttpServletRequest req){ ... } 

Lado del cliente:

 ... String jsonString = "{\"name\":\"myEnterprise\", \"departments\":[\"HR\"]}"; Enterprise enterprise = gson.fromJson(jsonString, Enterprise.class); System.out.println(gson.toJson(enterprise)); response = webResource .type(MediaType.APPLICATION_JSON) .put(ClientResponse.class,enterprise); if (response.getStatus() >= 400) { throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); } ... 

Esta es la solución para mi vieja pregunta:

Implementé mi propio ContextResolver para habilitar la función DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY.

 package org.lig.hadas.services.mapper; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; @Produces(MediaType.APPLICATION_JSON) @Provider public class ObjectMapperProvider implements ContextResolver { ObjectMapper mapper; public ObjectMapperProvider(){ mapper = new ObjectMapper(); mapper.configure(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); } @Override public ObjectMapper getContext(Class< ?> type) { return mapper; } } 

Y en el web.xml registré mi paquete en la definición del servlet …

  ... com.sun.jersey.spi.container.servlet.ServletContainer  com.sun.jersey.config.property.packages ...;org.lig.hadas.services.mapper  ...  

… todo el rest está hecho de manera transparente por jersey / jackson.

lo intentas

 [{"name":"myEnterprise", "departments":["HR"]}] 

la llave cuadrada es el punto clave.

Establecer este atributo en la instancia de ObjectMapper funciona,

 objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); 

Para las personas que encuentran esta pregunta buscando el mensaje de error, también puede ver este error si cometen un error en sus anotaciones @JsonProperty , de manera que anoten una propiedad de tipo List con el nombre de un campo de un solo valor:

 @JsonProperty("someSingleValuedField") // Oops, should have been "someMultiValuedField" public List getMyField() { // deserialization fails - single value into List return myField; }