Подтвердить что ты не робот

Невозможно десериализовать экземпляр java.util.ArrayList из VALUE_STRING

У меня есть служба REST, построенная с использованием Джерси и развернутая в AppEngine. Служба REST реализует глагол PUT, который потребляет тип приложения /json media. Связывание данных выполняется Jackson.

Глагол использует отношение предприятий-департаментов, представленных в JSON, как

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

На стороне клиента я использую gson для преобразования представления JSON в объект java. Затем я передаю объект в свою службу REST, и он отлично работает.

Проблема:

Когда у моего представления JSON есть только один элемент в коллекции

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

служба не может десериализовать объект.

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

Как сообщают другие пользователи, решение должно добавить флаг ACCEPT_SINGLE_VALUE_AS_ARRAY (например, Джерси: нельзя десериализовать экземпляр ArrayList из строки). Тем не менее, я не контролирую ObjectMapper, потому что на стороне обслуживания он прозрачно сделан Jackson.

Вопрос:

Есть ли способ настроить ObjectMapper на стороне службы, чтобы включить ACCEPT_SINGLE_VALUE_AS_ARRAY? аннотации? web.xml?

Детали кода

Объект Java:

@XmlRootElement
public class Enterprise {
    private String name;
    private List<String> departments;

    public Enterprise() {}

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<String> getDepartments() {
        return departments;
    }
    public void setDepartments(List<String> departments) {
        this.departments = departments;
    }
}

Сторона службы REST:

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

Клиентская сторона:

...
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());
}
...
4b9b3361

Ответ 1

Это решение для моего старого вопроса:

Я реализовал свой собственный ContextResolver, чтобы включить функцию 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>
{
   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;
   }
}

И в web.xml я зарегистрировал свой пакет в определении сервлета...

<servlet>
    <servlet-name>...</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>...;org.lig.hadas.services.mapper</param-value>        
    </init-param>
    ...
</servlet>

... все остальное прозрачно сделано джерси/джексоном.

Ответ 2

Установка этого атрибута в экземпляр ObjectMapper работает,

objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);

Ответ 3

попробуйте

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

квадратная скобка является ключевой точкой.

Ответ 4

Для людей, которые находят этот вопрос, выполнив поиск сообщения об ошибке, вы также можете увидеть эту ошибку, если вы допустили ошибку в своих аннотациях @JsonProperty, чтобы аннотировать свойство List -typed с именем одного -значное поле:

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

Ответ 5

из Jackson 2.7. x+ есть способ аннотировать саму переменную-член:

 @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
 private List<String> newsletters;

Более подробная информация здесь: Джексон @JsonFormat