无法将VALUE_STRING反序列化为java.util.ArrayList的实例

70

我使用Jersey构建了一个REST服务,并在AppEngine上部署。该REST服务实现了使用application/json媒体类型的PUT谓词。数据绑定由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: org.mortbay.jetty.HttpParser$Input@5a9c5842; line: 1, column: 2

根据其他用户的报告,解决方法是添加标记ACCEPT_SINGLE_VALUE_AS_ARRAY(例如,Jersey: Can not deserialize instance of ArrayList out of String)。然而,我没有控制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());
}
...

可能是重复的问题:Jackson反序列化 - 包含ArrayList<T> - Brian Roach
我同意。可能是一样的。然而,我的具体问题是如何设置ACCEPT_SINGLE_VALUE_AS_ARRAY标志,如果我没有管理ObjectMapper的话...如果可能的话。 - Manolo
5个回答

60

这是我之前问题的解决方案:

我自己实现了一个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>
    <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>

...其它的工作都由Jersey/Jackson透明地完成。


3
新版本:objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); - Filomat

30

将此属性设置为ObjectMapper实例即可生效。

objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);

6
他的问题是如何在没有访问对象管理器的情况下设置那个标志。 - Tom

25

你尝试过吗

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

方括号是关键点。


2
问题出在“部门”级别。这是Jackson的一个特殊情况,我通过创建自己的ContextResolver解决了它。 - Manolo

19
自 Jackson 2.7.x+ 起,有一种方法可以对成员变量本身进行注释:
 @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
 private List<String> newsletters;

更多信息请点击此处:Jackson @JsonFormat


6

如果您通过搜索错误消息找到此问题,那么您也可能会在@JsonProperty注释中犯错误,例如使用单值字段的名称注释List类型的属性:

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

“反序列化”不是利用setter而不是getter吗?据我所知,getter在序列化期间被访问。 - xetra11
反序列化可能使用setter或反射,但据我所记,按照惯例注释在getter上。 - Andrew Spencer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接