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
大功告成!解决了历史数据的问题。