一次FastJSON的填坑之旅

一、问题

今天线上的数据透出的时候突然出现了一些莫名其妙的数据,导致前端页面出现很多的数据找不到的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
[
{
"age":1,
"name":"yupeibiao"
},
{
"age":2,
"name":"xiaonian"
},
{
"$ref":"$[1]"
}
]
  • 数据是否一致? 但是很奇怪的是,预发(正式测试环境)的数据是好的,而只有线上的数据是坏的,于是首先想到的是线上跟预发的数据是不是不一样?

    失望的是,预发跟线上的不管是DB、搜索引擎以及kvstore等等数据存储方式的数据都是同一套,不可能存在预发跟线上数据不一致的情况。

  • 数据处理是否区分环境 既然排查了数据不一致的问题,那么接下来只能看代码里面有没有对数据的获取来源、处理来源等等数据处理或者获取方式可能存在问题,是否区分环境。

    于是只能查看代码了,因为接手应用不久,应用的代码也经过了很多的修改,而且每个对外透出的数据居然都是Map的方式,找起来异常的艰难。

  • 是否有环境处理开关 终于找到了几个开关,在metabaseyou进行数据配置,因为metabase是环境隔离的,在预发以及线上进行配置的diff,终于发现是一个开关不一样,预发是关闭的,而线上是开启的,而这个开关是用来判断是否开启缓存的,为了解决问题,直接把预发的数据同步到线上。

二、解决方式

但是到现在还是没有找到为什么会出现,“$ref”: “$[1]”这种奇怪的透出数据,看到ref第一反应是引用,会不会是引用有问题呢?

那数据添加进返回的Map是正常的,会不会是序列化问题呢?于是翻看 FastJSON的wiki,发现官方的解释如下:fastjson文档

看到官方文档,再看看我的返回数据,一切就像拨开云雾见月明的感觉,从文档可以看到,问题的出现是因为我们有相同的数据,奇怪的“$ref”: “$[1]”代表的意思是,此处取到的数据是这个列表的第一个数据同一个,它是一个引用,指向了第一个数据。而fastjson序列化的时候有很多的序列化方式,如果需要避免出现重复引用,序列化应该选择SerializerFeature.DisableCircularReferenceDetect序列化方式,该种方式可以解决循环引用的问题,而默认是不开启的,所以会出现上图的问题。

另外的一种方式是:BeanUtils.copyProperties(oldobject, newobject);改成对象新对象拷贝旧对象信息,将新对象存入列表的方式也可以解决问题。

使用这两种方式之后,数据也正常了,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
   [
{
"age":1,
"name":"yupeibiao"
},
{
"age":2,
"name":"xiaonian"
},
{
"age":2,
"name":"xiaonian"
}
]

三、测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* Created by buzheng on 18/1/10.
*/
public class Test {

public static void main(String[] args) {
Data d1 = new Data();
Data d2 = new Data();
d1.setAge(1);
d1.setName("yupeibiao");
d2.setName("xiaonian");
d2.setAge(2);
List<Data> list = new ArrayList<>();
list.add(d1);
list.add(d2);
list.add(d2);
//正确的方式
String s = JSON.toJSONString(list, SerializerFeature.DisableCircularReferenceDetect);
//错误的方式
String s = JSON.toJSONString(list);
return;



}
static class Data{
public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

int age;
String name;
}
}
坚持原创技术分享,您的支持将鼓励我继续创作!