通过本文可以大致了解一下 fastjson JSON.toJavaObject() 方法的几个坑、限制 和 避免方式。
JSON 类 大致提供了三类方法
- toJSON… 对象转 json
- parse… json 转 对象
- toJavaObject JSON 类转 POJO
toJavaObject 的实现
toJavaObject(JSON, Class<T>) 实际调用 TypeUtils.cast(Object, Class<T>, ParserConfig),
参数是 Object 类型,理论上可以传任何参数,但是根据方法实现,只能传以下几类,否则会报 can not cast to ...
- Map 的实现
- Map 的实现集合
- 基本数据类型
- …
所以不建议直接使用 TypeUtils 类,通过 JSON.toJavaObject(JSON, Class<T>) 调用的话,由于 JSON 类实现了 Map 接口,所以是符合 TypeUtils.cast 第一个参数的限制的
toJavaObject 第二个 Class 参数处理
如果通过 使用 JSON.toJavaObject,基本上会走到 TypeUtils.cast(Object, Class<T>, ParserConfig) 的以代码片段
1 | ... |
TypeUtils.castToJavaBean(Map<String, Object>, Class, ParserConfig)
该方法在 1.2.9 之前 和 1.2.9 及其之后 的实现上有所区别,对外表现的行为也不同,相同的代码
1 | Map<String, Object> singletonMap = Collections.singletonMap("asd", 123); |
1.2.9 之前
输出空 HashMap1
2{asd=123}
{}
大致逻辑是实例化 Class<T> 后,找到其中的 get/set 方法,因为 HashMap 并没有相应 get/set 的方法,实例化之后并没有设置值。
1.2.9 及其以后
报错:1
2
3
4
5
6
7{asd=123}
Exception in thread "main" com.alibaba.fastjson.JSONException: can not get javaBeanDeserializer
at com.alibaba.fastjson.util.TypeUtils.castToJavaBean(TypeUtils.java:917)
at .....
Caused by: com.alibaba.fastjson.JSONException: can not get javaBeanDeserializer
at com.alibaba.fastjson.util.TypeUtils.castToJavaBean(TypeUtils.java:912)
... 1 more
大致逻辑是 根据 Class<T> 找到对应的 ObjectDeserializer 实现,之后只针对 JavaBeanDeserializer 和 ASMJavaBeanDeserializer 实现进行处理。
1 | ... |
个人感觉报错比 1.2.9 之前 的返回空对象要好,原因在于
- 1.2.9 之前,返回值与预期想要的值是不一致的,如果不仔细测试,可能某些场景下很难发现这个问题,容易把问题带到生产环境
- 1.2.9 及其以后,fastjson 提前把问题暴露出来,在开发节点即可发现,提早发现提早解决
- 不过在升级 fastjson 的时候需要注意,由于行为上的不一致,可能原来隐藏的一个问题,通过异常的形式暴露的出来,有一定的风险
总结
toJavaObject(JSON, Class<T>)的第二个参数只能是- Map.class | 集合 (不能是 HashMap / JSONObject … )
- 简单 Java 对象(POJO)类型 | 集合
- …
一个错误示例
1 |
|
错误示例拆解
JSON.toJavaObject(JSON.parseObject(json), HashMap.class) 拆解后
JSONObject jsonObject = JSON.parseObject(jsonStr);JSON json = jsonObject;JSON.toJavaObject(json, HashMap.class);TypeUtils.cast(json, HashMap.class, ParserConfig)TypeUtils.castToJavaBean(Map<String, Object>, HashMap.class, ParserConfig)throw new JSONException("can not get javaBeanDeserializer");
建议方式
实际上 先把 json 字符串穿换成 JSON 对象,再 case JSON 对象成 指定的 class,步骤稍微有点多余
建议直接使用以下两种方式
JSON.parseObject(jsonStr, HashMap.class);JSON.parseObject(jsonStr, new TypeReference<HashMap<String, Integer>>() {})支持泛型