杰克逊不能反序列化空数组

Posted

技术标签:

【中文标题】杰克逊不能反序列化空数组【英文标题】:Jackson Can not deserialize empty array 【发布时间】:2014-09-29 17:42:23 【问题描述】:

我正在阅读 Facebook Insights 并试图让 Jackson 将 JSON 映射到 Object。如果所有数据都没有空,我让它工作。但是我在尝试反序列化键值的空数组时遇到问题。即使尝试了这个帖子:How to prevent null values inside a Map and null fields inside a bean from getting serialized through Jackson 也没有解决问题:(

这是 JSON:

"data":["id":"492640667465465\/insights\/page_fans_country\/lifetime","name":"page_fans_country","period":"lifetime","values":["value":"MY":26315,"ID":311,"SG":77,"NP":63,"MM":56,"PH":51,"GB":44,"US":44,"KR":36,"TH":36,"IN":34,"BD":24,"PK":22,"BN":22,"AU":15,"TW":14,"VN":12,"KH":11,"YE":11,"CA":10,"JP":10,"EG":8,"ZA":7,"SA":6,"ES":6,"HK":6,"FR":6,"IT":5,"IL":5,"IR":5,"NG":5,"LK":5,"BR":5,"IQ":4,"AF":4,"AE":4,"GT":4,"RO":4,"LR":4,"RU":4,"PS":4,"DE":4,"CN":4,"LY":3,"JO":3,"end_time":"2014-08-02T07:00:00+0000","value":"MY":26326,"ID":315,"SG":77,"NP":63,"MM":56,"PH":54,"GB":44,"US":43,"TH":38,"KR":36,"IN":33,"BD":23,"BN":22,"PK":21,"AU":16,"TW":14,"VN":12,"KH":11,"YE":11,"CA":10,"JP":10,"EG":8,"ZA":7,"SA":7,"ES":6,"HK":6,"FR":6,"IT":5,"IL":5,"IR":5,"NG":5,"LK":5,"BR":5,"IQ":4,"RU":4,"CN":4,"GT":4,"RO":4,"LR":4,"AF":4,"PS":4,"DE":4,"AE":4,"LY":3,"CH":3,"end_time":"2014-08-03T07:00:00+0000","value":"MY":26338,"ID":312,"SG":79,"NP":63,"MM":55,"PH":52,"US":45,"GB":44,"TH":39,"KR":34,"IN":32,"BD":24,"BN":22,"PK":21,"AU":16,"TW":14,"KH":12,"VN":12,"CA":11,"YE":11,"JP":10,"EG":8,"ZA":7,"SA":7,"ES":6,"HK":6,"FR":6,"IT":5,"CN":5,"IR":5,"NG":5,"LK":5,"BR":5,"IL":5,"IQ":4,"AF":4,"AE":4,"GT":4,"RO":4,"LR":4,"RU":4,"PS":4,"DE":4,"NZ":3,"TR":3,"end_time":"2014-08-04T07:00:00+0000"],"title":"Lifetime Likes by Country","description":"Lifetime: Aggregated Facebook location data, sorted by country, about the people who like your Page. (Unique Users)","id":"492640667465465\/insights\/page_storytellers_by_country\/day","name":"page_storytellers_by_country","period":"day","values":["value":[],"end_time":"2014-08-02T07:00:00+0000","value":[],"end_time":"2014-08-03T07:00:00+0000","value":[],"end_time":"2014-08-04T07:00:00+0000"],"title":"Daily Country: People Talking About This","description":"Daily: The number of People Talking About the Page by user country (Unique Users)","id":"492640667465465\/insights\/page_storytellers_by_country\/week","name":"page_storytellers_by_country","period":"week","values":["value":"MY":136,"IN":3,"ID":2,"BD":1,"US":1,"TN":1,"AU":1,"end_time":"2014-08-02T07:00:00+0000","value":"MY":131,"IN":3,"US":1,"TN":1,"AU":1,"ID":1,"end_time":"2014-08-03T07:00:00+0000","value":"MY":118,"IN":2,"KH":1,"TR":1,"US":1,"TN":1,"AR":1,"AU":1,"end_time":"2014-08-04T07:00:00+0000"],"title":"Weekly Country: People Talking About This","description":"Weekly: The number of People Talking About the Page by user country (Unique Users)","id":"492640667465465\/insights\/page_storytellers_by_country\/days_28","name":"page_storytellers_by_country","period":"days_28","values":["value":"MY":492,"IN":5,"ID":3,"AU":2,"SG":2,"ZA":2,"US":2,"GB":2,"RO":1,"PH":1,"NP":1,"BD":1,"JO":1,"PS":1,"TN":1,"IR":1,"CA":1,"CN":1,"KR":1,"end_time":"2014-08-02T07:00:00+0000","value":"MY":499,"IN":5,"ID":3,"GB":2,"SG":2,"ZA":2,"US":2,"RO":1,"PH":1,"NP":1,"BD":1,"AU":1,"CN":1,"KR":1,"TN":1,"IR":1,"CA":1,"JO":1,"end_time":"2014-08-03T07:00:00+0000","value":"MY":501,"IN":4,"ID":3,"SG":2,"ZA":2,"US":2,"GB":2,"AU":1,"RO":1,"PH":1,"NP":1,"JO":1,"AR":1,"KR":1,"BD":1,"TR":1,"IR":1,"CA":1,"CN":1,"KH":1,"TN":1,"end_time":"2014-08-04T07:00:00+0000"],"title":"28 Days Country: People Talking About This","description":"28 Days: The number of People Talking About the Page by user country (Unique Users)"],"paging":"previous":"https:\/\/graph.facebook.com\/v2.0\/492640667465465\/insights?since=1406649169&until=1406908369","next":"https:\/\/graph.facebook.com\/v2.0\/492640667465465\/insights?since=1407167569&until=1407426769"

我当前的代码根本不喜欢这样 --> "value":[]

以下是我的对象:

import java.util.Date;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

public class Insights 
private Data[] data;
private Paging paging;

public Data[] getData() 
    return data;

public void setData(Data[] data) 
    this.data = data;

public Paging getPaging() 
    return paging;

public void setPaging(Paging paging) 
    this.paging = paging;





/**
 * inner class for Data
 * @author pohsoon.yap
 *
 */
public static class Data 
    private String id;
    private String name;
    private String period;
    private Values[] values;
    private String title;
    private String description;

    public String getId() 
        return id;
    
    public void setId(String id) 
        this.id = id;
    
    public String getName() 
        return name;
    
    public void setName(String name) 
        this.name = name;
    
    public String getPeriod() 
        return period;
    
    public void setPeriod(String period) 
        this.period = period;
           
    public Values[] getValues() 
        return values;
    
    public void setValues(Values[] values) 
        this.values = values;
    
    public String getTitle() 
        return title;
    
    public void setTitle(String title) 
        this.title = title;
    
    public String getDescription() 
        return description;
    
    public void setDescription(String description) 
        this.description = description;
    


    /**
     * inner class for Values
     * @author pohsoon.yap
     *
     */
    public static class Values 
        // if "value":[]  then this will break
        private Map<String, Integer> Value;
        private String end_time;
        public Map<String, Integer> getValue() 
            return Value;
        
        public void setValue(Map<String, Integer> value) 
            Value = value;
        
        public String getEnd_time() 
            return end_time;
        
        public void setEnd_time(String end_time) 
            this.end_time = end_time;
        


    


public static class Paging 
    private String previous;
    private String next;

    public String getPrevious() 
        return previous;
    
    public void setPrevious(String previous) 
        this.previous = previous;
    
    public String getNext() 
        return next;
    
    public void setNext(String next) 
        this.next = next;
    


我的代码sn-p如下:

    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
    List<Insights> insightList = new ArrayList();
    String insightStr = "";
    try 

        for (Operation operation : mq.getOperationList())

            String apiEndPoint = this.facebookGraphApiUrl + operation.getApi();
            apiEndPoint = apiEndPoint.replace("pageid", mq.getFacebookPage().getPageId());
            uri = new URI(apiEndPoint);
            insightStr = facebook.getApi().restOperations().getForObject(uri, String.class);

            Insights insights = mapper.readValue(insightStr, Insights.class);

完整的堆栈跟踪:

com.fasterxml.jackson.databind.JsonMappingException:无法从 START_ARRAY 令牌中反序列化 java.util.LinkedHashMap 的实例 在 [来源:java.io.StringReader@625a80df;行:1,列:1603](通过参考链:com.social.facebook.model.Insights["data"]->com.social.facebook.model.Data["values"]->com.social.facebook .model.Values["value"]) 在 com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164) 在 com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:599) 在 com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:593) 在 com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:306) 在 com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:26) 在 com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375) 在 com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98) 在 com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308) 在 com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) 在 com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:147) 在 com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:18) 在 com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375) 在 com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98) 在 com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308) 在 com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) 在 com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:147) 在 com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:18) 在 com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375) 在 com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98) 在 com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308) 在 com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) 在 com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2796) 在 com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1942)

【问题讨论】:

JSON value 属性似乎是一个数组类型,但您的模型类需要一个映射。我想知道 value 属性不为空时的样子? @AlexeyGavrilov 在我上面附加的同一个 JSON 中,“数据”数组有 4 个项目,其中只有一个项目的值为空,其余的看起来像这样:“值”:“ MY":26315,"ID":311 我认为是一张地图;一个键和一个值? 【参考方案1】:

正如其他人所解释的,您正在尝试将 JSON Array 映射到 Java Map,这是默认情况下不允许的。

但可能允许空 JSON 数组映射到 java.util.Map。通过启用DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT:

objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT);

这至少在 POJO 类型的情况下有效;我不记得这是否适用于通常采用 JSON 对象的其他 Java 类型。

【讨论】:

【参考方案2】:

模型中的value 字段声明为Map,而相应的JSON 属性可以是空数组或键值映射。 Jackson 无法将空数组分配给地图字段。

假设您希望在客户端解决问题,您可以修改setValue 方法以接受泛型Object,然后验证它是映射还是数组(实际上是List,因为Jackson 反序列化数组作为 Java 集合)。这是一个例子:

public class JacksonArrayAsMap 

    public static class Bean 
        private Map<String, Object> value;

        public void setValue(Object value) 
            if (value instanceof Map) 
                this.value = (Map<String, Object>) value;
             else if (value instanceof List && ((List) value).size() == 0)
                this.value = Collections.EMPTY_MAP;
             else 
                throw new IllegalArgumentException("Invalid value: " + value);
            
        

        @Override
        public String toString() 
            return "Bean" +
                    "value=" + value +
                    '';
        
    

    public static void main(String[] args) throws IOException 
        final String json1 = "\"value\":";
        final String json2 = "\"value\":[]";
        final String json3 = "\"value\":\"a\":\"b\"";
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.readValue(json1, Bean.class));
        System.out.println(mapper.readValue(json2, Bean.class));
        System.out.println(mapper.readValue(json3, Bean.class));
    

输出:

Beanvalue=
Beanvalue=
Beanvalue=a=b

【讨论】:

感谢您的回答,我戴上我的爱好帽后会试试这个:) 耶!确认它有效 :) 我将继续处理来自 Graph API 的其他可用 JSON 提要。

以上是关于杰克逊不能反序列化空数组的主要内容,如果未能解决你的问题,请参考以下文章

如何用杰克逊反序列化空字符串?

杰克逊将单个项目反序列化为列表

杰克逊:地图的反序列化

有啥方法可以防止杰克逊中的字段反序列化?

JSON杰克逊序列化反序列化列表列表

杰克逊反序列化阵列通量