Fastjson反序列化随机性失败

发布时间:2022-11-30 12:00

本文主要讲述了一个具有"随机性"的反序列化错误!

前言

Fastjson作为一款高性能的JSON序列化框架,使用场景众多,不过也存在一些潜在的bug和不足。本文主要讲述了一个具有"随机性"的反序列化错误!

问题代码

为了清晰地描述整个报错的来龙去脉,将相关代码贴出来,同时也为了可以本地执行,看一下实际效果。

  StewardTipItem

package test;


import java.util.List;


public class StewardTipItem {


    private Integer type;


    private List contents;


    public StewardTipItem(Integer type, List contents) {
        this.type = type;
        this.contents = contents;
    }
}

  StewardTipCategory

反序列化时失败,此类有两个特殊之处:

  1. 返回StewardTipCategory的build方法(忽略返回null值)。

  2. 构造函数『C1』Map> items参数与List items属性同名,但类型不同!

package test;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;


public class StewardTipCategory {


    private String category;


    private List items;


    public StewardTipCategory build() {
        return null;
    }


    //C1 下文使用C1引用该构造函数
    public StewardTipCategory(String category, Map> items) {
        List categoryItems = new ArrayList<>();
        for (Map.Entry> item : items.entrySet()) {
            StewardTipItem tipItem = new StewardTipItem(item.getKey(), item.getValue());
            categoryItems.add(tipItem);
        }
        this.items = categoryItems;
        this.category = category;
    }


    // C2 下文使用C2引用该构造函数
    public StewardTipCategory(String category, List items) {
        this.category = category;
        this.items = items;
    }


    public String getCategory() {
        return category;
    }


    public void setCategory(String category) {
        this.category = category;
    }


    public List getItems() {
        return items;
    }


    public void setItems(List items) {
        this.items = items;
    }
}

  StewardTip

package test;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;


public class StewardTip {


    private List categories;


    public StewardTip(Map>> categories) {
        List tipCategories = new ArrayList<>();
        for (Map.Entry>> category : categories.entrySet()) {
            StewardTipCategory tipCategory = new StewardTipCategory(category.getKey(), category.getValue());
            tipCategories.add(tipCategory);
        }
        this.categories = tipCategories;
    }


    public StewardTip(List categories) {
        this.categories = categories;
    }


    public List getCategories() {
        return categories;
    }


    public void setCategories(List categories) {
        this.categories = categories;
    }
}

  JSON字符串

{
    "categories":[
        {
            "category":"工艺类",
            "items":[
                {
                    "contents":[
                        "工艺类-提醒项-内容1",
                        "工艺类-提醒项-内容2"
                    ],
                    "type":1
                },
                {
                    "contents":[
                        "工艺类-疑问项-内容1"
                    ],
                    "type":2
                }
            ]
        }
    ]
}

  FastJSONTest

package test;


import com.alibaba.fastjson.JSONObject;


public class FastJSONTest {


    public static void main(String[] args) {
        String tip = "{\"categories\":[{\"category\":\"工艺类\",\"items\":[{\"contents\":[\"工艺类-提醒项-内容1\",\"工艺类-提醒项-内容2\"],\"type\":1},{\"contents\":[\"工艺类-疑问项-内容1\"],\"type\":2}]}]}";
        try {
            JSONObject.parseObject(tip, StewardTip.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  堆栈信息

当执行FastJSONTest的main方法时报错:

com.alibaba.fastjson.JSONException: syntax error, expect {, actual [
  at com.alibaba.fastjson.parser.deserializer.MapDeserializer.parseMap(MapDeserializer.java:228)
  at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:67)
  at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:43)
  at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:85)
  at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:838)
  at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:288)
  at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:284)
  at com.alibaba.fastjson.parser.deserializer.ArrayListTypeFieldDeserializer.parseArray(ArrayListTypeFieldDeserializer.java:181)
  at com.alibaba.fastjson.parser.deserializer.ArrayListTypeFieldDeserializer.parseField(ArrayListTypeFieldDeserializer.java:69)
  at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:838)
  at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:288)
  at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:672)
  at com.alibaba.fastjson.JSON.parseObject(JSON.java:396)
  at com.alibaba.fastjson.JSON.parseObject(JSON.java:300)
  at com.alibaba.fastjson.JSON.parseObject(JSON.java:573)
  at test.FastJSONTest.main(FastJSONTest.java:17)

问题排查

排查过程有两个难点:

  1. 不能根据报错信息得到异常时JSON字符串的key,position或者其他有价值的提示信息。

  2. 报错并不是每次执行都会发生,存在随机性,执行十次可能报错两三次,没有统计失败率。



经过多次执行之后还是找到了一些蛛丝马迹!下面结合源码对整个过程进行简单地叙述,最后也会给出怎么能在报错的时候debug到代码的方法。

  JavaBeanInfo:285行

Fastjson反序列化随机性失败_第1张图片

clazz是StewardTipCategory.class的情况下,提出以下两个问题:

Q1:Constructor[] constructors数组的返回值是什么?

Q2:constructors数组元素的顺序是什么?

参考java.lang.Class#getDeclaredConstructors的注释,可得到A1:

Fastjson反序列化随机性失败_第2张图片

  • A1

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号