别给我讲源码,告诉我Moshi是如何实现反序列化的!

"都用上Kotlin了,不会还在用Gson解析数据吧"java

Moshi是什么

一句话描述json

Moshi就是一个实现了对Json序列化和反序列化的开源库api

大名鼎鼎的Gson你们确定都知道,Moshi本质上就是干了和他同样的事安全

( 本文是创建在对Moshi使用有一些简单了解的基础上,若是尚未了解过Moshi,建议看下这篇文章 新一代Json解析库Moshi使用及原理解析 )markdown

为何使用Moshi

一句话描述svn

Gson是一个针对Java的Json序列化和反序列化库函数

Moshi是一个针对Kotlin的Json序列化和反序列化库post

在各类更高级语言涌现的今天,特别是kotlin的诞生,虽然本质上仍是java那一套,但各类方便易用的api和语法糖,让人大呼爽歪歪。而老牌的Gson在处理Kotlin序列化问题上显得有些力不从心,主要有两个问题:flex

(1)空安全问题

Kotlin对变量分为可空类型和不可空类型,而Java中基本上全部变量都是可空的(除了@NonNullable标签)。若是Json中一个变量值是null,但Kotlin声明这个变量是非空,Gson仍然会把这个变量赋值为nullui

(2)默认参数失效

默认参数是Kotlin的一个新语法。是在定义一个data类时,直接在构造函数中赋给参数默认值。例如:

class People(val name:String, val age:Int = 18)
复制代码

当Json是:

{
    "name" : "哈哈哈"
}
复制代码

根据Kotlin的语法,指望获得的对象是

People(name="哈哈哈",age = 18)
复制代码

但实际上Gson会解析成

People(name="哈哈哈",age = 0)
复制代码

这样就形成默认参数age = 18 的丢失

为了让Json解析更加符合kotlin的语法规范,因而选择使用Moshi

Moshi是如何实现反序列化的

这时候好奇的哥哥可能就要问了

为何Moshi的解析更符合Kotlin的语法规范呢? 你说符合就符合啊?

别急别急,想弄清这个问题,须要了解一下Moshi是如何实现反序列化的

咱们先看一下Moshi反序列化的简单使用:

// 须要反序列化的json
val json = "{
    "name": "张三",
    "age": 18,
    "email": "helloword@163.com"
}"

// Model类
class People(val name:String,val age:Int ){
val email : String = ""
}

// 反序列化
fun parseModel(json: String): People? {
    val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()
    val adapter = moshi.adapter(People::class.java)
    val people = adapter.fromJson(json)
    return people
}
复制代码

( Model类里藏了一个坑,后面揭晓 ^ ^ )

没有用过Moshi的哥哥可能很反感这种行为,“来不来就给我整几行代码?我是看了代码就能记住的人吗?!”

别急别急,我这就一点一点给您掰开了揉碎了说,您就舒舒服服躺好嘞~

这里陌生的东西主要有两个。KotlinJsonAdapterFactoryadapter

KotlinJsonAdapterFactory 是生成JsonAdapter的工厂

val adapter = moshi.adapter(People::class.java)实际上就是传了个class type给KotlinJsonAdapterFactory,而后工厂建立出adapter

③ adapter调用本身的fromJsontoJson方法完成序列化和反序列化过程。

把上面的代码翻译成流程图就是:

Moshi反序列化流程图

Moshi反序列化过程就是这么简单!

校验对象

了解了Moshi的反序列化流程,咱们来回答一下上面哥哥的问题

为何说用Moshi更符合kotlin语法规范呢?

咱们先回到Moshi使用的那个例子,这个反序列化的结果是什么呢?

People(name="张三", age = 18) email = "helloworld@163.com"

仍是 People(name="张三", age = 18) email = ""

咱们从Kotlin语法分析,emailval 不可变的。序列化实际上就是建立一个对象而后根据json进行赋值,那么Moshi会对不可变的 email 进行赋值吗

答案是不会。

胆大心细脸皮厚的哥哥可能已经看到上面流程图中KotlinJsonAdapterFactory 在生成 JsonAdapter过程当中,有一个「检验对象是否合法」的过程,我偷偷贴这个过程当中的一行源码:

// 位于:KotlinJsonAdapterFactory#create()

 if (property !is KMutableProperty1 && parameter == null) continue

复制代码

哎哎哎,我不是标题党,这不是源码,是英语英语,不信我给你解释解释

propery -- 表示数据类中全部的成员属性。用上面的例子来讲就是val name:Stringval age:Intvar email : String = ""

KMutableProperty1 -- 是Kotlin中的类型对象。表示的是var这个可变类型

parameter -- 表示构造函数中的参数。用上面的例子就是val name:String,val age:Int(这里联系上下文知道 parameter == null 是用来判断 property 是否是构造函数中声明的成员属性)

continue -- 跳过。不对这个属性反序列化了

因此用伟大中华人民共和国56个民族之一的汉族语言来讲就是:

若是一个成员属性,他是不可变的(也就是val)而且不是构造函数中声明的,就跳过它

看到了吧,KotlinAdapterJsonFactory 实际上就是区别于Gson的关键之一,它对类对象作了一些校验,使Moshi更加符合Kotlin语法

真正实现

KotlinJsonAdapter是Moshi的另外一个核心,是Model的序列化和反序列化的真正实现

adapter的核心是binding

这里咱们能够类比一下Model。一个Model对应一个adapter,Model中的成员属性就是一个个Binding

Binding负责基本数据类型的解析。IntBinding里面有个IntAdapter,负责解析json中的int类型。StringBinding里有个StringAdapter,负责解析json中的string类型。

总的来讲,Binding就是负责拿到json中字段的类型和值,而后本身保存起来

至于json中字段的类型和值是如何被识别的嘛,Moshi基本上就是抄Gson的JsonReader和JsonWriter

KotlinJsonAdapter经过反射生成一个类对象,这些binding把成员属性的值一个个赋值到属性上,最终生产出一个model对象。

等等等等,经过反射生成对象?咋这么抽象呢?

很简单啦。其实就是调用Android的Constructor类中newInstance(*args)方法

只要有类对象,生成实例对象就是一句话的事

People::class.java.constructors[1].newInstance("张三", 20)

// constructors[0]是参数最多的构造函数
// 第三个参数表示第几个构造参数属性用默认参数的值
// 因此结果是:People("张三", 18)
People::class.java.constructors[0].newInstance("张三", 20, 2, null)

复制代码

到目前为止,咱们基本上顺了一遍Moshi反序列化的过程。尽可能不贴大段代码,是不但愿你们在好不容易啃完一遍源码以后,过几个星期就感受似是而非,似懂非懂

因此我但愿能给你们一个简单的体系, 有一些不得不用源码的地方也是尽可能给出通俗的解释。

代码只是细节的实现,是知识体系上的枝叶。若是你们想再深刻了解某些细节,能够阅读如下方法

// factory生成adapter
KotlinJsonAdapterFactory#create()

// adapter序列化和反序列化:
KotlinJsonAdapter#fromJson
KotlinJsonAdapter#toJson

// 反射生成对象
KCallableImpl#callBy#callDefaultMethod
CallerImpl>Constructor#call
Constructor#newInstance
复制代码

我可没有贴代码哦,是大家本身要读的~

我是方木

不喜欢大段源码

想把技术用通俗简单的语言描述清楚

想写一些本身快乐,你们快乐的东西

欢迎各位点赞关注~~

相关文章
相关标签/搜索