废话不多说,直接上正文
-
坑1:我们初始化Map之类的集合的时候会用如下优雅的方式:
Map<String, String> map = new HashMap<String, String>() { { put("cat", "cat"); } }; Gson gson = new Gson(); System.out.println(gson.toJson(map));
但是会发现序列化后为
null
;这是因为上述方式产生的map是匿名内部类的实例
,也就是说new出来的map
没有类名。原因:com.google.gson.internal.Excluder#create
,看源码得知Gson
不序列化匿名和局部类。为何要这样设计呢?在github/gson的issues中找到了相关的描述(gson/issues/298),这里好像是说内部类的适配器会生成对外部类/实例的隐式引用,会导致循环引用(英语不好,应该是这样描述…)
-
坑2:
Map<String, String> map = new HashMap<String, String>() { { put("cat", "cat"); } }; Set<Map.Entry<String, String>> set2 = map.entrySet(); Gson gson = new Gson(); System.out.println(gson.toJson(set2))
结果为
[{}]
,如上也是无法转换json的,原因看源码:com.google.gson.internal.bind.ReflectiveTypeAdapterFactory#getBoundFields
if (raw.isInterface()) { // 这里raw是“集合元素”的类型,为java.util.Map.Entry,是一个接口! return result; }
这里的
map
就算是普通的map
(不使用构造代码块),也有一样的问题。因此,集合的泛型不要用接口、匿名内部类(匿名内部类会有坑1的问题,序列化结果为[null]
)。 -
阿里的
fastjson
不会有上述问题稍微看一下
fastjson
的底层实现:com.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializer(java.lang.Class<?>)
这里调用了几个方法:
buildBeanInfo
,这里有个参数fieldBased
默认为false
,因此会调用computeGetters
方法createASMSerializer(beanInfo)
大概可以看出来在序列化的时候,先利用反射找到对象类的所有get方法,接下来去
get
,然后小写化,作为json的每个key值,而get方法的返回值作为value。接下来再反射field,添加到json中。
附:上文的测试代码 > study-metis/json-test