fastjson的JSON.toJSONString方法的一些坑
背景
在项目工程为了定位Kafka消费进度的问题,有个字段a类型是Map<TopicPartition, OffsetAndMetadata>,增加打印a字段数据的日志,打印方式是JSON.toJSONString(a),但是日志打印出来的结果却是{{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{},{}:{}}。
原因

因为TopicPartition的字段都是私有的,而且没有对应的getter方法,json反序列化为了安全考虑,不会反序列化这种字段。因此要想通过JSON.toJSONString打印出想要的字段,就必须将字段改成public或增加getter方法。
测试
定义类:
`
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
public class FastjsonExample {
public static void main(String[] args) {
Test1 test1 = new Test1(1, "tom");
Test2 test2 = new Test2(2, "jack");
Test3 test3 = new Test3(2, "jack");
test2.address = "beijing";
test3.address = "beijing";
// 作为单独对象
System.out.println("test1:" + JSON.toJSONString(test1));
System.out.println("test2:" + JSON.toJSONString(test2));
System.out.println("test3:" + JSON.toJSONString(test3));
Map<Test1, Test1> map1 = new HashMap<>();
map1.put(test1, test1);
Map<Test2, Test2> map2 = new HashMap<>();
map2.put(test2, test2);
Map<Test3, Test3> map3 = new HashMap<>();
map3.put(test3, test3);
// 放入map中
System.out.println("map1:" + JSON.toJSONString(map1));
System.out.println("map2:" + JSON.toJSONString(map2));
System.out.println("map3:" + JSON.toJSONString(map3));
System.out.println("map1 with Feature:" + JSON.toJSONString(map1, JSONWriter.Feature.WriteNonStringKeyAsString, JSONWriter.Feature.WriteNonStringValueAsString));
System.out.println("map2 with Feature:" + JSON.toJSONString(map2, JSONWriter.Feature.WriteNonStringKeyAsString, JSONWriter.Feature.WriteNonStringValueAsString));
System.out.println("map3 with Feature:" + JSON.toJSONString(map3, JSONWriter.Feature.WriteNonStringKeyAsString, JSONWriter.Feature.WriteNonStringValueAsString));
}
/**
* 全private字段,且没有getter方法
*/
static class Test1 implements Serializable {
private static final long serialVersionUID = 2019555404968089681L;
public Test1(int offset, String name) {
this.offset = offset;
this.name = name;
}
private final int offset;
private final String name;
@Override
public String toString() {
return "ToString{offset=" + offset + ", name='" + name + '}';
}
}
/**
* 部分private字段,部分public字段,部分getter方法
*/
static class Test2 implements Serializable {
private static final long serialVersionUID = 2019555404968089681L;
public Test2(int offset, String name) {
this.offset = offset;
this.name = name;
}
private final int offset;
private final String name;
public String address;
public int getOffset() {
return offset;
}
@Override
public String toString() {
return "ToString{offset=" + offset + ", name='" + name + ", address='" + address + '}';
}
}
/**
* 部分private字段,部分public字段,部分getter方法,不重写toString方法
*/
static class Test3 implements Serializable {
private static final long serialVersionUID = 2019555404968089681L;
public Test3(int offset, String name) {
this.offset = offset;
this.name = name;
}
private final int offset;
private final String name;
public String address;
public int getOffset() {
return offset;
}
}
}
`
结果:
test1:{}
test2:{"address":"beijing","offset":2}
test3:{"address":"beijing","offset":2}
map1:{{}:{}}
map2:{{"address":"beijing","offset":2}:{"address":"beijing","offset":2}}
map3:{{"address":"beijing","offset":2}:{"address":"beijing","offset":2}}
map1 with Feature:{"ToString{offset=1, name='tom}":{}}
map2 with Feature:{"ToString{offset=2, name='jack, address='beijing}":{"address":"beijing","offset":"2"}}
map3 with Feature:{"com.test.fastjson.FastjsonExample$Test3@5a42bbf4":{"address":"beijing","offset":"2"}}
解释
test1结果为空:Test1全private字段,且没有getter方法,因此无法反序列化这些字段。
test2结果中没有name字段:因为Test2的name字段为private字段,且没有getter方法,因此无法反序列化该字段,其他字段正常。
test3结果中没有name字段:原因同上。
map1结果为空:Test1全private字段,且没有getter方法,因此无法反序列化这些字段。
map2结果中没有name字段:因为Test2的name字段为private字段,且没有getter方法,因此无法反序列化该字段,其他字段正常。
map3结果中没有name字段:原因同上。
map1 with Feature结果key为toString方法结果,value为空:使用WriteNonStringKeyAsString特性时,会使用toString方法返回的结果作为key,不会再使用Test1的json字符串作为key;value为空是因为只会使用Test1的json字符串作为value,不会使用toString方法的返回值。
map2 with Feature结果key为toString方法结果,value为json串:原因同上。
map3 with Feature结果key为test3对象的内存地址,value为json串:原因跟上面一样,补充一句:这个key值其实也是toString方法的返回,只是因为Test3没有重写toString方法,所以输出的是对象的内存地址。
结论
现在编程因为注解方便或者习惯化的原因,一般字段都有getter方法,因此我们习惯性觉得JSON.toJSONString方法肯定能把内部的字段值打印出来,但不保证所有的第三方组件都有getter方法,而且无脑打印整个对象的内容也不是一种好的日志输出方式,日志打印应该只打印真正关心的字段值。

浙公网安备 33010602011771号