前言:
刚转后端没多久,在通过RPC
调用其他组的服务的时候,其他组对接的同学说,你的入参是有问题的,然后他把入参发我,类似于下面的 json:
{
"addressMO": {
"roomNumber": "1002",
"street": "上海市徐汇区田林街道"
},
"age": 10,
"brotherMO": {
"addressMO": {
"$ref": "$.addressMO"
},
"className": "上海中学",
"name": "我是哥哥"
},
"name": "我是弟弟"
}
这个对象包含一个 addressMO
和 brotherMO
以及 name
和 age
,brotherMO 中也包含了addressMO,就会导致 出现上面的 "$ref": "$.addressMO"
,这个其实表示的是引用根目录中的 addressMO
对象。
这个例子其实是我简化后的 demo
,为了简单说明。其他的 对象如下:
public static class PersonMO {
private String name;
private int age;
private AddressMO addressMO;
private BrotherMO brotherMO;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public AddressMO getAddressMO() {
return addressMO;
}
public void setAddressMO(AddressMO addressMO) {
this.addressMO = addressMO;
}
public BrotherMO getBrotherMO() {
return brotherMO;
}
public void setBrotherMO(BrotherMO brotherMO) {
this.brotherMO = brotherMO;
}
}
public static class AddressMO {
private String street;
private String roomNumber;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getRoomNumber() {
return roomNumber;
}
public void setRoomNumber(String roomNumber) {
this.roomNumber = roomNumber;
}
}
public static class BrotherMO {
private String name;
private String className;
private AddressMO addressMO;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public AddressMO getAddressMO() {
return addressMO;
}
public void setAddressMO(AddressMO addressMO) {
this.addressMO = addressMO;
}
}
测试代码如下:
@Test
public void testJSON() {
AddressMO addressMO = new AddressMO();
addressMO.setStreet("上海市徐汇区田林街道");
addressMO.setRoomNumber("1002");
BrotherMO brotherMO = new BrotherMO();
brotherMO.setName("我是哥哥");
brotherMO.setAddressMO(addressMO);
brotherMO.setClassName("上海中学");
PersonMO personMO = new PersonMO();
personMO.setName("我是弟弟");
personMO.setAge(10);
personMO.setAddressMO(addressMO);
personMO.setBrotherMO(brotherMO);
String ss = JSON.toJSONString(personMO);
System.out.println("ss ===== " +ss);
}
可以看到,对于一个对象,当其首次出现时,FastJson
的序列化是正常工作的,但是当其重复出现时,就会变为对象的引用。这是FastJson
内部存在一些特殊逻辑,问题产生的原因在于FastJson
存在循环/重复引用检测特性,并且该特性是缺省开启的。
实体转化为json字符串后出现了$ref
字样的东西,这是因为在传输的数据中出现相同的对象时,fastjson 默认开启引用检测将相同的对象写成引用的形式,引用是通过"$ref"
来表示的。
修改也很简单,就是在 BrotherMO
中设置 AddressMO
对象是,通过 Copy AddressMO
对象的属性就行了,如下:
就能正常输出了,其中的 DozerBeanMapper
是开源库,用来复制对象属性的。
{
"addressMO": {
"roomNumber": "1002",
"street": "上海市徐汇区田林街道"
},
"age": 10,
"brotherMO": {
"addressMO": {
"roomNumber": "1002",
"street": "上海市徐汇区田林街道"
},
"className": "上海中学",
"name": "我是哥哥"
},
"name": "我是弟弟"
}
其实不改也是没问题的,通过反序列化出来的 PersonMO
对象都是正常的。
刚开始,其他同事跟我说的时候我是很懵逼的,完全不知道,因为我调用他的服务之前是加了日志的,都是正常的,但是同事说的是这个服务上线2年多了,肯定不是他们的问题,哈哈,然后 google 了下,记录下,怕自己会忘记。