接着分析json-iterator的源码,首先从序列化函数入手
它的定义位于github.com/json-iterator/go@v1.1.12/adapter.go
其中ConfigDefault是一个全局变量:github.com/json-iterator/go@v1.1.12/config.go
其中定义了各种pool和cache提供缓存能力来提升性能:
接口我们看下它的序列化函数,它调用了WriteVal
github.com/json-iterator/go@v1.1.12/reflect.go,它通过反射获取类型,然后通过类型找到对应的编码函数,最后通过Encode方法进行编码,查找过程中优先从cache里获取:
如果没有找到,就创建一个
可以看到,它优先根据类型,获取RawMessahge,JsonNumber,Marshaler,Any,Native几种类型的编码函数,获取不到,根据Kind,获取基本类型的编码函数:
如果是Interface类型,它使用dynamicEncoder,定义位于:github.com/json-iterator/go@v1.1.12/reflect_dynamic.go
struct类型的encoder定义位于:
github.com/json-iterator/go@v1.1.12/reflect_struct_encoder.go
它先获取结构体类型的描述信息,然后通过遍历描述信息的字段,以及绑定的名称,最后进行每个字段的递归处理。
json数字的处理定义位于:github.com/json-iterator/go@v1.1.12/reflect_json_number.go,里面有两个分支,分布对应json的Number和jsoniter的Number类型。
他俩都是通过反射获取的对象的类型信息:
对应的,解码函数定义如下:
字符串原位赋值,不解析,所以没有内存分配和释放,速度很快,不错的想法。指针指向json中的位置。编码函数其实就是简单地将函数转换成字符串。
类似的浮点数处理也一样,先按照字符串读取,这样可以避免内存分配,然后按需解析:github.com/json-iterator/go@v1.1.12/iter_float.go
类型的解析,会通过map来缓存类型和对应解析函数的关联关系:github.com/json-iterator/go@v1.1.12/reflect_extension.go
对于结构体来说,describeStruct通过反射获取类型,然后解析出其描述信息,对于它的每个field可以进行递归处理:
最后创建 StructDescriptor,的实例:
其中用到的反射并不是golang原生的反射,而是新封装的:
github.com/modern-go/reflect2@v1.0.2/reflect2.go,它实现了如下接口:
介绍完序列化函数,我们来分析下反序列化函数:
它的入口位于:github.com/json-iterator/go@v1.1.12/config.go,调用了迭代器的ReadVal函数:
具体定义位于:github.com/json-iterator/go@v1.1.12/reflect.go,先从cache获取decoder,获取失败,通过反射获取类型信息,然后通过类型获取decoder,最后调用decoder的Decode方法来进行json解析:
如果缓存拿不到,就创建这种类型的decoder:
和获取编码函数的方式类似:
自定义类型的解码器如果没有找到,就根据Kind,获取基本类型的解码器:
获取类型的过程,的TypeOf是通过指针直接获取的:github.com/modern-go/reflect2@v1.0.2/reflect2.go
github.com/modern-go/reflect2@v1.0.2/unsafe_eface.go
获取类型的解码函数后,我们就可以通过iter迭代器来沿着json字符串进行解析了:github.com/json-iterator/go@v1.1.12/iter.go,比如nextToken用于过滤空格
下面详细介绍下结构体的解码器是如何定义的:github.com/json-iterator/go@v1.1.12/reflect_struct_decoder.go,先获取结构体的描述信息,然后根据描述信息里的Fieds,递归获取解码器,然后通过map缓存,方便后面获取:
注意这里有个优化,在字段少于10个的结构体,直接在内存定义字段key的hash值和对应的解码函数,匹配的时候可以通过hash值匹配,然后找到解码函数,不用走map的申请和匹配,提升速度。超过10个则退化到map查找:
decoder的定义是个递归的过程:
值的解码定义位于:github.com/json-iterator/go@v1.1.12/reflect.go
native类型的解码器定义位于:github.com/json-iterator/go@v1.1.12/reflect_native.go,可以看到,对于每一种基础类型,都定义了对应的解码器:
bool解码器,本质上就是把json字符串中的true,false解析成bool值:
类似浮点数:
浮点数的解析过程中,直接通过字符的值,最后计算出浮点数的值:
github.com/json-iterator/go@v1.1.12/reflect_extension.go结构体的描述信息是通过反射,获取结构体的字段信息,最后组建的:
json迭代器的定义位于github.com/json-iterator/go@v1.1.12/iter.go
read函数不断向后读取字符,并根据获得的key,找到val对应的类型和对应的解析函数,最后调用解析函数,完成解析和绑定:
比如结构体的解析位于:
github.com/json-iterator/go@v1.1.12/reflect_struct_decoder.go
可以看到每个字段的解析过程
先解析出结构体每个field的标识,存入标识到解析方法的影射的map里,供后面解析使用。解析的时候,解析出json的对象后,根据key string到field里面去查,时间复杂度O(1),得到value的解析函数,如此递归。
小于10个的时候,自己做了hash,避免了,map的寻址,直接进行结构体的字段和hash值匹配,从json里直接计算key的hash,通过内存运算加速。
超过10个字段,使用上面的通用decoder来解码:
超过10个字段的场景,依次遍历当前json对象内部的所有字段,尝试进行,和go 结构体的每个field进行匹配,field照样是到map里面去取:
github.com/json-iterator/go@v1.1.12/reflect_extension.go可以看到结构体描述信息是如何组装的:
组装完成后会对描述信息排序,方便后面加速查找:
本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!