Java的JSON库有很多,本文分析google的Gson和alibaba的fastjson,在Java泛型场景反序列化的一些有意思的行为。考虑下面的json字符串:

["2147483648","2147483647"
]

用fastjson在不指定类型的情况下解析,下面的代码输出啥:

JSON.parseArray(s).forEach(o -> { System.out.println(o.getClass()); });

答案是:

class java.lang.Long
class java.lang.Integer

是不是感觉有点儿奇怪,两个都是数字啊,居然输出了不同的类型,原因我们下面细讲。再看看Gson, 用Gson解析并且不指定泛型类型的话,下面的代码输出啥:

new Gson().fromJson(s, List.class).forEach(o -> { System.out.println(o.getClass()); });

答案是:

class java.lang.Double
class java.lang.Double

这次两个都是Double类型,明明是整数啊,为啥到Gson这里变成Double了?

我们试了两个Json库,解析相同的json字符串,得到全然不同的结果。如果在实际使用的时候,用这种方式反序列化JSON,很容易出现BUG,而且是运行时才可能出现,这种问题一旦出现往往很难排查。因此为了保证泛型类型Json反序列化的正确性,一定要明确指定泛型的类型。下面我们先看看正确的解析应该怎么写,再深度探讨一下内部的原理。

正确的泛型Json反序列化

fastjson和Gson都提供了泛型类型的反序列化方案,先来看看fastjson,对于上面的case,正确的反序列化代码如下:

JSON.parseObject(s, new TypeReference<List<Long>>(){})
.forEach(o -> { System.out.println(o.getClass()); });

创建一个确定泛型类型的TypeReference子类(这里是匿名内部类),将这个子类传递给fastjson以帮助fastjson在运行时获得泛型的具体类型信息,从而实现泛型正确反序列化。Gson的方案与fastjson相同,或者应该反过来说fastjson的方案与Gson相同。大家感兴趣的话可以看看fastjson的TypeReference和Gson的TypeToken代码,基本上fastjson就是抄袭Gson,连注释都抄了......。Gson指定泛型类型的反序列化方法如下,也是创建一个确定泛型类型的匿名子类:

new Gson().<List<Long>>fromJson(s, new TypeToken<List<Long>>(){}.getType())
.forEach(o -> { System.out.println(o.getClass()); });

由于fastjson的方案是来自Gson,以下只讨论Gson的泛型反序列化原理。为什么泛型的反序列化显得这么麻烦呢,非要通过子类化的方式,不能直接告诉Gson泛型的类型吗?是的,Java是真的做不到,其实理由很简单,泛型类既然是泛型,意味着就不应该带有具体泛型类型的信息,因此泛型类的字节码本身就不应该也无法保存泛型类型,但是子类如果继承一个明确泛型类型的父类(父类是一个泛型类型,Gson里面就是TypeToken),子类必须保存父类的明确类型信息,通过Class类的getGenericSuperclass方法能够获得父类类型信息,该类型信息包含了父类具体的泛型类型信息。这个方法帮助Java的泛型体系完整化了,是非常重要的一个方法。

类库设计分析

面对相同的设计问题,fastjson与Gson在很多点上的选择不同,借此机会可以一窥类库设计的思想,让我们一一来看。

是静态方法还是实例化

使用Gson之前,必须进行实例化,Gson提供了两种方式:一种是无参数构造器,一种是通过GsonBuilder,后者能够进行更多的定制,但无论是哪种方法,都需要实例化一个Gson对象。但是Fastjson使用之前是不需要实例化的,直接使用JSON类的静态方法即可实现json序列化和反序列化。这一点上来讲,Fastjson比较方便,虽然Gson是线程安全的,可以用static变量来声明一个Gson实例(饿汉模式的单例)然后全局使用,但是还是比Fastjson多了一步。但是Gson这么做的好处是如果序列化(反序列化)的定制比较多,可以在初始化的时候完成复杂的扩展定制,使用的时候依然保持简单,Fastjson就需要每次都传递额外的参数来实现。总体来讲各有优化,Gson是线程安全的,大部分场景都是定义全局的静态单例,用起来跟Fastjson差不多。Gson在这里的选择倾向于希望对外的接口保持一致和简单,即无论怎么定制逻辑,Json的序列化和反序列化就那么几个方法,内部逻辑可以定制,但是使用接口不用改变。

数字的默认类型

对于数字类型,如果没有明确指定类型,Gson默认都解析成Double类型,而Fastjson会根据数字的不同,解析成Long、Integer或者BigDecimal。我们在生产中用Fastjson就遇到这种问题:由于集合没有指定泛型类型,反序列化的时候,不同大小的数字被反序列化成了不同的类型,导致业务逻辑出错。这种未制定类型情况下,感觉Gson的处理更合适一些,既然未指定类型,对外的默认类型始终是Double,接口对外的心智更稳定。

集合类型反序列化

对于列表类型的反序列化,Fastjson提供了parseArray系列方法,这样很多情况下可以避免使用TypeReference,代码写起来更简单。但是Gson就没有这种方法,如果需要解析列表,必须使用TypeToken<List<Xxx>>,并没有为列表设置特殊的方法,这里依然能看到 Gson希望对外的接口保持一致和简单 ,即便牺牲一点儿方便性。

泛型反序列化

为了解析泛型,Gson和Fastjson都提供了类似的机制(Gson使用TypeToken承载类型,而Fastjson使用TypeReference承载类型),利用子类继承确定泛型类型父类的方式,获得类型,区别是Gson的接口只接受Type类型的参数,不接受TypeToken参数,这是因为Type是JDK的自带类型,这种设计的效果是Gson的接口非常简单。Fastjson的接口可以支持Type参数,也支持TypeReference参数。

小结

整体上能明显看出来fastjson更多是长出来的,接口多而全,应该是不断有人提需求的结果,而Gson是设计出来的,接口的一致性很强,高内聚低耦合,有些时候宁愿牺牲接口的便利性,也要保证接口对外的一致性、简单和概念完整,从设计上我是崇尚Gson的设计理念的,但实际的开发过程更容易演变成fastjson的模式,在中国程序员的地位真的不够高。

参考资料

[1]. fastjson源代码
[2]. Gson源代码
[3]. https://github.com/google/gson/blob/master/GsonDesignDocument.md

Json反序列化与Java泛型相关推荐

  1. Java中含有泛型的 JSON 反序列化问题

     作者:明明如月学长 blog.csdn.net/w605283073/article/details/107350113 一.背景 今天无聊之园提了一个问题,涉及的示例大致如下: public st ...

  2. 当Java泛型擦除遇到JSON序列化和反序列化

    当Java泛型类型擦除遇到JSON序列化和反序列化 目录 当Java泛型类型擦除遇到JSON序列化和反序列化 前言 测试 前言 -最近看到了Spring 关于 RestTemplate的源码实现又有了 ...

  3. 含有泛型的 JSON 反序列化问题

    含有泛型的 JSON 反序列化 一.背景 二.分析 2.1 事出诡异必有妖 三.解决之道 3.1 猜想验证 3.2 举一反三 四.总结 一.背景 今天无聊之园提了一个问题,涉及的示例大致如下: pub ...

  4. java 泛型集合 json_将泛型集合转换成分页json数据

    [java]代码库package com.sunweb.util.jsonfactory; import java.util.List; import com.sunweb.util.jsonfact ...

  5. java 获取泛型_聊聊Java泛型擦除那些事

    >版权申明]非商业目的注明出处可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/89789849 出自:shushen ...

  6. Jackson将json字符串转换成泛型List/map

    Jackson将json字符串转换成泛型List/map url: http://www.cnblogs.com/quanyongan/archive/2013/04/16/3024993.html ...

  7. java中将json字符串_Java中JSON字符串与java对象的互换实例详解

    在开发过程中,经常需要和别的系统交换数据,数据交换的格式有XML.JSON等,JSON作为一个轻量级的数据格式比xml效率要高,XML需要很多的标签,这无疑占据了网络流量,JSON在这方面则做的很好, ...

  8. Java中JSON字符串与java对象的互换实例详解

    这篇文章主要介绍了在java中,JSON字符串与java对象的相互转换实例详解,非常不错,具有参考借鉴价值,需要的朋友可以参考下 在开发过程中,经常需要和别的系统交换数据,数据交换的格式有XML.JS ...

  9. java 泛型参数的类型_Java获得泛型参数类型

    在Android开发中,使用Gson将json字符串转换为Java对象尤为常见.在这个转换过程中,通常会结合泛型参数.接口或者抽象类来封装处理. T t = new Gson().fromJson(r ...

最新文章

  1. IE8下的VML显示问题解决方案
  2. luogu cover
  3. JQueryEasyUI validatebox 扩展其自带验证方法
  4. 2013国家二级c语言上机考试点了编译并运行出现黑框闪退,2013年计算机二级C语言上机试题及解析2...
  5. MFC 教程【2_MFC和Win32 】
  6. lvs的dr和nat模式配置备忘
  7. 超强阵容实操教学, 0 基础 Flink 训练营免费抢报啦!
  8. Photoshop2019 安装破解
  9. HashMap常见面试题
  10. 2021-01-09
  11. 1.1 电阻 RES Resistance
  12. ps在html中的应用程序,Photoshop在网页设计中的应用
  13. Postman两个接口数据关联
  14. 利用注册表处理计算机故障骤,第七章WINDOWS注册表及其维护-江苏大学.ppt
  15. 0417-Autosar AP总结
  16. web服务验证事件合法请求,使用到了__VIEWSTATE、__EVENTVALIDATION、cookie来验证
  17. 计算机不安装操作系统能安装应用软件吗,win10禁止安装,详细教您win10如何设置禁止安装软件...
  18. 【操作系统】思维导图
  19. 计算机专业就业率最低: 正规军干不过游击队收藏 面对当今的研究生教育——只有无奈我国教育中令人揪心的若干个不等式...
  20. 在画电路图时,想问下几种地之间的区别? power-GND singal-GND GND

热门文章

  1. python 列表 元组 字符串
  2. linux下yum包更新不了
  3. 一般处理程序使用Session报错(未将对象引用设置到对象实例)
  4. WordPress 多个安全漏洞
  5. SQL:RAND()返回随机数
  6. struts1 几种Action父类的比较
  7. 8.腾讯微博Android客户端开发——自动获取验证码(2)
  8. cms是什么意思啊_GC 知识点补充——CMS
  9. PowerShell-4.API调用以及DLL调用
  10. POJ1135比较有意思的对短路(多米骨牌)