阿里妹导读
本篇将主要介绍json序列化的详细流程。本文阅读的FastJSON源码版本为2.0.31。
一、引言
在日常开发中,我们常用FastJSON进行序列化和反序列化。虽然它给我们带来了便捷,但其背后的原理往往被忽视,于是一个不小心就引发了很多血案,例如:
在不知其所以然的情况下,我每次使用起来也是胆战心惊的,比如抛出我经常遇到的两个问题:
1.序列化操作:JSON.toJSONString()方法
代码中许多地方都使用了JSON.toJSONString()方法打印日志,可能会遇到转换失败的情况,比如下面一段报错:
注:阿里巴巴开发规约中已经明确禁止在日志打印中直接用JSON工具将对象转换成String,所以大家还是尽量避免使用。
2.反序列化操作:JSON.parseObject()方法
当一个类中嵌套了多层内部类时,JSON.parseObject() 方法是否能够准确转换?
因此,我决定遵循“深入了解才能安心使用”的原则,阅读一下 FastJSON 的源码,以便更好地理解其原理和使用时需要注意的事项。
由于内容较长,本篇将主要介绍json序列化的详细流程。本文阅读的FastJSON源码版本为2.0.31。
二、FastJSON源码阅读
1. FastJSON整体结构
FastJSON的源码结构如下,其中JSON类是源码的入口类,虽然看起来有很多方法,实则概括起来是四类方法:
FastJSON中几个关键的包为:annotation包、asm包、parser包、serializer包。
2. 序列化流程
以JSON.toJSONString(),并且序列化一个JavaBean为例,整个方法序列化的时序图如下:
首先,用户调用JSON.toJSONString() 方法并且传入待序列化对象,随后执行以下序列化流程:
1.创建SerializeWriter 对象:SerializeWriter对象类似于StringBuilder ,但性能上做了许多优化,用来存储序列化过程中产生的字符串。
3.序列化器为 null 的情况:
4.写入数据: 获取到序列化写入器后,JSONSerializer 调用该写入器的 write() 方法开始将数据写入 SerializeWriter。
5.返回 JSON 字符串: 最后,SerializeWriter 将存储的字符串转换为最终的 JSON 字符串,并返回给用户。
通过这些步骤,用户最终得到了序列化后的 JSON 字符串。
下面详细介绍一下具体的代码实现流程:
1.调用JSON.toJSONString(obj)方法后,最终会走到下面的重载函数,这个函数主要干了三件事:
a.创建SerializeWriter对象 out,用于储存在序列化过程中产生的数据。
b.创建JSONSerializer 对象serializer,该对象持有所有负责具体对象序列化工作类的引用。
2.其中serializer对象的write方法如下,这个方法主要做了两件事:
3.获取对象的解析类
getObjectWriter方法会内部调用重载方法,且create参数为true。
这个方法会创建出JavaBeanSerializer对象和SerializeBeanInfo对象,SerializeBeanInfo主要是包含了java对象的字段和方法信息以及注释等,它决定了一个java对象序列化过程的输出。JavaBeanSerializer会根据SerializeBeanInfo对象中的fields字段,创建对应的成员变量的FieldSerializer。
ii.computeGetters方法的具体内容如下,通过判断以get开头和is开头的方法返回元素属性的集合。
5.调用解析类的write方法序列化对象

首先会根据IgnoreNonFieldGetter属性选择是否忽略没有对应字段的get方法,然后通过调用每个属性序列化器的getPropertyValueDirect方法获得对应的值。
三、序列化注意事项
1. getter方法规范化
FastJSON序列化元素时是通过调用对象的所有以“get”或者“is”开头的方法实现的,根据上文源码中实现逻辑,应该注意以下几点:
举个🌰:类MyTest定义了一个没有对应成员变量的getNoField()方法,并且方法里有错误代码,根据源码逻辑,序列化过程中一定会调用这个方法。
SerializerFeature.IgnoreNonFieldGetter参数,则可以序列化成功。
2. 布尔类型属性命名不要用is开头
布尔类型的属性不要使用is开头,fastJons会截取is后面的名称当作属性名,造成序列化错误的情况。
举个🌰:自定义类MyTest中包含属性isActive和value,另外还写了一个没有对应成员变量的getNoField方法。
3. 重复引用问题
序列化过程如果存在重复/循环引用的情况,FastJSON就会用引用标识\(ref代替,这样做的好处一是可以节省空间,二是可以避免循环引用的问题。举个🌰:</span></p><div style="line-height:1.75em;margin-bottom:24px;margin-top:24px"><span style="color:rgb(62, 62, 62);font-size:15px">这段代码的输出结果如下:可以看到重复引用的user1变成了{"\)ref":"$[0]"}格式。
注意,这种字符串格式只有使用FastJSON才可以正确反序列化,当涉及到是有了不同的序列化框架时往往会导致失败,如果想要正常的序列化方式化,可以通过:
禁止使用循环引用。
四、附录
这里给出序列化时常用到注解和SerializerFeature序列化属性。
最后,由于个人水平有限,如文章中有误解或疏漏之处,欢迎各位大佬批评指正~
1.注解
注解
含义
参数
@JSONField
该注解作用于方法上,字段上和参数上,可在进行序列化和反序列化时进行个性功能定制。
@JSonType
该注解作用于类上,对该类的字段进行序列化和反序列化时的特性功能定制。
@JSONCreator
用于标识一个类的构造函数或静态工厂方法,以便在反序列化 (将 JSON 数据转换为 Java 对象) 时使用。
无
@JSONPOJOBuilder
用于定义POJO的构建器类和构建方法,使得创建复杂对象变得灵活。
2.SerializerFeature枚举类
黄色标记的为常用枚举值。
常量
含义
QuoteFieldNames
是否输出key值引号,默认为 true
UseSingleQuotes
输出是否使用单引号,默认为 false

WriteMapNullValue
是否输出值为 null 的字段,默认为 false
WriteEnumUsingToString
是否输出枚举值的 toString() 方法,默认为 false
是否使用 ISO8601 格式输出日期,默认为 false
WriteNullListAsEmpty
是否将 null 值的 List 输出为空数组,默认为 false
WriteNullStringAsEmpty
是否将 null 值的 String 输出为空字符串,默认为 false
WriteNullNumberAsZero
是否将 null 值的 Number 输出为 0,默认为 false
WriteNullBooleanAsFalse
是否将 null 值的 Boolean 输出为 false,默认为 false
SkipTransientField
是否跳过 transient 修饰的字段,默认为 false
SortField
是否按照字段名称排序输出,默认为 false
WriteTabAsSpecial
是否将制表符输出为 ,默认为 false
PrettyFormat
是否格式化输出,默认为 false
WriteClassName
是否输出对象的 class 名称,默认为 false
DisableCircularReferenceDetect
是否禁止循环引用检测,默认为 false
WriteSlashAsSpecial
是否将斜杠输出为 /,默认为 false
BrowserCompatible
是否输出为浏览器兼容的 JSON 格式,默认为 false
WriteDateUseDateFormat
是否按照指定的日期格式输出日期,默认为 false
DisableCheckSpecialChar
是否禁止特殊字符检测,默认为 false
NotWriteDefaultValue
是否不输出默认值字段,默认为 false
BeanToArray
是否将 JavaBean 转换为数组输出,默认为 false
WriteNonStringKeyAsString
是否将非 String 类型的 key 转换为 String 类型输出,默认为 false
NotWriteRootClassName
是否不输出根对象的 class 名称,默认为 false
DisableASM
是否禁用 ASM 库进行序列化,默认为 false
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/166766.html