Imansible deserializzare l’istanza di java.util.ArrayList su VALUE_STRING

Ho un servizio REST costruito con Jersey e distribuito in AppEngine. Il servizio REST implementa il verbo PUT che utilizza un tipo di applicazione / json media. L’associazione dei dati viene eseguita da Jackson.

Il verbo consuma una relazione reparti aziendali rappresentata in JSON come

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

Dal lato client, utilizzo gson per convertire la rappresentazione JSON in un object java. Quindi, passare l’object al mio servizio REST e funziona correttamente.

Problema:

Quando la mia rappresentazione JSON ha solo un elemento nella collezione

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

il servizio non può deserializzare l’object.

 ATTENTION: /enterprise/enterprise: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token at [Source: [email protected]; line: 1, column: 2 

Come riportato da altri utenti, la soluzione è aggiungere il flag ACCEPT_SINGLE_VALUE_AS_ARRAY (ad esempio, Jersey: imansible deserializzare l’istanza di ArrayList su String ). Tuttavia, non sto controllando un ObjectMapper perché nel lato del servizio è reso trasparente da Jackson.

Domanda:

C’è un modo per configurare ObjectMapper sul lato del servizio per abilitare ACCEPT_SINGLE_VALUE_AS_ARRAY? annotazioni? web.xml?

Dettagli del codice

Oggetto 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; } } 

Il lato del servizio REST:

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

Dalla parte 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()); } ... 

Questa è la soluzione per la mia vecchia domanda:

Ho implementato il mio ContextResolver per abilitare la funzione 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; } } 

E nel web.xml ho registrato il mio pacchetto nella definizione del servlet …

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

… tutto il resto è fatto in modo trasparente da jersey / jackson.

ci provi

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

la parentesi quadra è il punto chiave.

L’impostazione di questo attributo sull’istanza di ObjectMapper funziona,

 objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); 

Per le persone che trovano questa domanda cercando il messaggio di errore, puoi anche vedere questo errore se @JsonProperty un errore nelle tue annotazioni @JsonProperty tale che annoti una proprietà di tipo List con il nome di un campo a valore singolo:

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