杰克逊反序列化 - 包含ArrayList< T> [英] Jackson deserialization - with contained ArrayList<T>

查看:119
本文介绍了杰克逊反序列化 - 包含ArrayList< T>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

美好的一天,

我目前正在尝试使用Jackson(使用Jersey)来使用生成JSON(用.NET编写)的REST服务。 JSON由可能的错误消息和对象数组组成。下面是Jersey的日志记录过滤器返回的JSON示例:

I am currently integration attempting to consume a REST service that produces JSON (written in .NET) using Jackson (with Jersey). The JSON consists of a possible error message and an array of objects. Below is a sample of the JSON returned as produced by Jersey's logging filter:

{
    "error":null,
    "object":"[{\"Id\":16,\"Class\":\"ReportType\",\"ClassID\":\"4\",\"ListItemParent_ID\":4,\"Item\":\"Pothole\",\"Description\":\"Pothole\",\"Sequence\":1,\"LastEditDate\":null,\"LastEditor\":null,\"ItemStatus\":\"Active\",\"ItemColor\":\"#00AF64\"}]"
}

我有两个类代表类型(外部ListResponse):

I have two classes to represent the type (the outer ListResponse):

public class ListResponse { 

    public String error;    
    public ArrayList<ListItem> object;  

    public ListResponse() { 
    }
}

和(内部ListItem):

and (the inner ListItem):

public class ListItem {
    @JsonProperty("Id")
    public int id;      
    @JsonProperty("Class")
    public String classType;
    @JsonProperty("ClassID")
    public String classId;  
    @JsonProperty("ListItemParent_ID")
    public int parentId;    
    @JsonProperty("Item")
    public String item; 
    @JsonProperty("Description")
    public String description;

    @JsonAnySetter 
    public void handleUnknown(String key, Object value) {}

    public ListItem() {
    }
}

调用并返回JSON的类如下所示:

The class that invokes and returns the JSON looks like this:

public class CitizenPlusService {
    private Client client = null;   
    private WebResource service = null;     

    public CitizenPlusService() {
        initializeService("http://localhost:59105/PlusService/"); 
    }

    private void initializeService(String baseURI) {    
        // Use the default client configuration. 
        ClientConfig clientConfig = new DefaultClientConfig();      
        clientConfig.getClasses().add(JacksonJsonProvider.class);                       

        client = Client.create(clientConfig);

        // Add a logging filter to track communication between server and client. 
        client.addFilter(new LoggingFilter()); 
        // Add the base URI
        service = client.resource(UriBuilder.fromUri(baseURI).build()); 
    }

    public ListResponse getListItems(String id) throws Exception
    {           
        ListResponse response = service.path("GetListItems").path(id).accept(MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_XML_TYPE).get(ListResponse.class);                                  
        return response;            
    }
}

这里重要的调用是getListItems方法。在测试工具中运行代码,产生以下结果:

The important call here is the getListItems method. Running the code in a test harness, produces the following:

org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
at [Source: java.io.StringReader@49497eb8; line: 1, column: 14] (through reference chain: citizenplus.types.ListResponse["object"])

请协助。

问候,
Carl-Peter Meyer

Regards, Carl-Peter Meyer

推荐答案

你的问题是'object'属性值是一个String而不是一个数组!该字符串包含一个JSON数组,但Jackson期望一个原生数组(没有包装引号)。

Your problem is that the 'object' property value is a String and not an array! The string contains a JSON array but Jackson expects a native array (without the wrapping quotes).

我有同样的问题,我创建了一个自定义反序列化器,它将反序列化一个字符串值到所需类型的泛型集合:

I had the same problem and I created a custom deserializer, which will deserialize a string value to a generic collection of the desired type:

public class JsonCollectionDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {

  private final BeanProperty    property;

  /**
   * Default constructor needed by Jackson to be able to call 'createContextual'.
   * Beware, that the object created here will cause a NPE when used for deserializing!
   */
  public JsonCollectionDeserializer() {
    super(Collection.class);
    this.property = null;
  }

  /**
   * Constructor for the actual object to be used for deserializing.
   *
   * @param property this is the property/field which is to be serialized
   */
  private JsonCollectionDeserializer(BeanProperty property) {
    super(property.getType());
    this.property = property;
  }

  @Override
  public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
    return new JsonCollectionDeserializer(property);
  }


  @Override
  public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    switch (jp.getCurrentToken()) {
      case VALUE_STRING:
        // value is a string but we want it to be something else: unescape the string and convert it
        return JacksonUtil.MAPPER.readValue(StringUtil.unescapeXml(jp.getText()), property.getType());
      default:
        // continue as normal: find the correct deserializer for the type and call it
        return ctxt.findContextualValueDeserializer(property.getType(), property).deserialize(jp, ctxt);
    }
  }
}

请注意,此解串器也会如果值实际上是一个数组而不是字符串,则工作,因为它会相应地委托实际的反序列化。

Note that this deserializer will also work if the value actually is an array and not a string, because it delegates the actual deserialization accordingly.

在您的示例中,您现在必须注释您的集合字段,如此:

In your example you would now have to annotate your collection field like so:

public class ListResponse { 

    public String error;    
    @JsonDeserialize(using = JsonCollectionDeserializer.class)
    public ArrayList<ListItem> object;  

    public ListResponse() {}    
}

应该是它。

注意:JacksonUtil和StringUtil是自定义类,但您可以轻松替换它们。例如,使用 new ObjectMapper() org.apache.commons.lang3.StringEscapeUtils

Note: JacksonUtil and StringUtil are custom classes, but you can easily replace them. For example by using new ObjectMapper() and org.apache.commons.lang3.StringEscapeUtils.

这篇关于杰克逊反序列化 - 包含ArrayList&lt; T&gt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆