前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang源码分析:json-iterator/go(2)

golang源码分析:json-iterator/go(2)

作者头像
golangLeetcode
发布2023-09-06 19:18:43
3340
发布2023-09-06 19:18:43
举报

接着分析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可以看到结构体描述信息是如何组装的:

组装完成后会对描述信息排序,方便后面加速查找:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-03-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档