fastjson重复引用问题及解决办法

fastjson在进行序列化的时候,如果属性是同一个引用,会进行压缩,通过一些语法引用其他的json块,下面举例说明。

Product类是最外层的类,BaseInfo和BaseInfoChild是Product类的两个属性的类型,其中BaseInfoChild是BaseInfo的子类。

public class BaseInfo {
    private Long id;
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class BaseInfoChild extends BaseInfo{

}
public class Product {
    private BaseInfo baseInfo;
    private BaseInfoChild baseInfoChild;

    public BaseInfo getBaseInfo() {
        return baseInfo;
    }

    public void setBaseInfo(BaseInfo baseInfo) {
        this.baseInfo = baseInfo;
    }

    public BaseInfoChild getBaseInfoChild() {
        return baseInfoChild;
    }

    public void setBaseInfoChild(BaseInfoChild baseInfoChild) {
        this.baseInfoChild = baseInfoChild;
    }
}

当同一个BaseInfoChild子类的对象同时赋值给Product的baseInfo和baseInfoChild属性的时候,打印的json语句:

{"baseInfo":{"id":1,"name":"child"},"baseInfoChild":{"$ref":"$.baseInfo"}}

显然,因为是同一个对象引用,fastjson用自己的方式进行了压缩。

代码如下:

public class JSONTEST {
    public static void main(String[] args) {
        version1();
    }

    private static void version1(){
        Product p = new Product();

        BaseInfoChild child = new BaseInfoChild();
        child.setId(1L);
        child.setName("child");
        p.setBaseInfo(child);
        p.setBaseInfoChild(child);
        String jsonString = JSON.toJSONString(p);
        //{"baseInfo":{"id":1,"name":"child"},"baseInfoChild":{"$ref":"$.baseInfo"}}
        System.out.println(jsonString);
        //这一句会报异常
        Product pnew = JSON.parseObject(jsonString, Product.class);
    }
}

然而,当把该json进行反序列化的时候,就会报异常:

Exception in thread "main" com.alibaba.fastjson.JSONException: json.test1.test1.BaseInfo cannot be cast to json.test1.test1.BaseInfoChild
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:693)
	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 json.test1.test1.JSONTEST.version1(JSONTEST.java:26)
	at json.test1.test1.JSONTEST.main(JSONTEST.java:13)
Caused by: java.lang.ClassCastException: json.test1.test1.BaseInfo cannot be cast to json.test1.test1.BaseInfoChild
	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_3_BaseInfoChild.deserialze(Unknown Source)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:284)
	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_Product.deserialze(Unknown Source)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:284)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:688)
	... 5 more

此时,fastjson把json字符串中的内容的类型当做了BaseInfo,即使属性完全一样,也不能向下转型转成BaseInfoChild。

fastjson官方显然知道这种情况,官方给出了解决办法:

1.单个关闭 JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect);
2.全局配置关闭 JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();

转自:fastjson 重复引用和循环引用问题 - 云+社区 - 腾讯云

    private static void version2(){
        Product p = new Product();

        BaseInfoChild child = new BaseInfoChild();
        child.setId(1L);
        child.setName("child");
        p.setBaseInfo(child);
        p.setBaseInfoChild(child);
        String jsonString = JSON.toJSONString(p,SerializerFeature.DisableCircularReferenceDetect);
        System.out.println(jsonString);
        Product pnew = JSON.parseObject(jsonString, Product.class);
        System.out.println(p.getBaseInfoChild().getName());
    }
    private static void version3(){
        JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
        Product p = new Product();

        BaseInfoChild child = new BaseInfoChild();
        child.setId(1L);
        child.setName("child");
        p.setBaseInfo(child);
        p.setBaseInfoChild(child);
        String jsonString = JSON.toJSONString(p);
        System.out.println(jsonString);
        Product pnew = JSON.parseObject(jsonString, Product.class);
        System.out.println(p.getBaseInfoChild().getName());
    }

 以上两种方式,都可以在最后打印出正确的结果。

====

但是,如果你已经将json字符串存到了redis缓存,用的时候再去反序列化,此时上面的两种方法已经晚了,怎么办?

幸运的是,fastjson进行了压缩,但是其通过JSONObject的方式是可以获取压缩引用的部分,

    private static void version4(){
        Product p = new Product();

        BaseInfoChild child = new BaseInfoChild();
        child.setId(1L);
        child.setName("child");
        p.setBaseInfo(child);
        p.setBaseInfoChild(child);
        String jsonString = JSON.toJSONString(p);
        System.out.println("原json串:"+jsonString);
        JSONObject jsonObject = JSON.parseObject(jsonString);
        System.out.println("获取引用节点:"+jsonObject.get("baseInfoChild").toString());
        
    }

//打印结果为:
原json串:{"baseInfo":{"id":1,"name":"child"},"baseInfoChild":{"$ref":"$.baseInfo"}}
获取引用节点:{"name":"child","id":1}

 既然可以正常获取json内容,那么如果这个字符串变成常规的json串,直接反序列化是可以成功的。通过json将引用部分取出来,然后再当做普通的json内容复制到特定节点,常规的json串就生成了。

    private static void version4(){
        Product p = new Product();

        BaseInfoChild child = new BaseInfoChild();
        child.setId(1L);
        child.setName("child");
        p.setBaseInfo(child);
        p.setBaseInfoChild(child);
        String jsonString = JSON.toJSONString(p);
        System.out.println("原json串:"+jsonString);
        JSONObject jsonObject = JSON.parseObject(jsonString);
        System.out.println("获取引用节点:"+jsonObject.get("baseInfoChild").toString());
        jsonObject.put("baseInfoChild",JSON.parseObject(jsonObject.get("baseInfoChild").toString()));
        String jsonNew = jsonObject.toJSONString();
        Product pnew = JSON.parseObject(jsonNew, Product.class);

        System.out.println(p.getBaseInfoChild().getName());
    }

//打印结果:
原json串:{"baseInfo":{"id":1,"name":"child"},"baseInfoChild":{"$ref":"$.baseInfo"}}
获取引用节点:{"name":"child","id":1}
child

大功告成!解决了历史数据的问题。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值