"都用上Kotlin了,不会还在用Gson解析数据吧"java
一句话描述json
Moshi就是一个实现了对Json序列化和反序列化的开源库api
大名鼎鼎的Gson你们确定都知道,Moshi本质上就是干了和他同样的事安全
( 本文是创建在对Moshi使用有一些简单了解的基础上,若是尚未了解过Moshi,建议看下这篇文章 新一代Json解析库Moshi使用及原理解析 )markdown
一句话描述svn
Gson是一个针对Java的Json序列化和反序列化库函数
Moshi是一个针对Kotlin的Json序列化和反序列化库post
在各类更高级语言涌现的今天,特别是kotlin的诞生,虽然本质上仍是java那一套,但各类方便易用的api和语法糖,让人大呼爽歪歪。而老牌的Gson在处理Kotlin序列化问题上显得有些力不从心,主要有两个问题:flex
Kotlin对变量分为可空类型和不可空类型,而Java中基本上全部变量都是可空的(除了@NonNullable标签)。若是Json中一个变量值是null,但Kotlin声明这个变量是非空,Gson仍然会把这个变量赋值为nullui
默认参数是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的解析更符合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的哥哥可能很反感这种行为,“来不来就给我整几行代码?我是看了代码就能记住的人吗?!”
别急别急,我这就一点一点给您掰开了揉碎了说,您就舒舒服服躺好嘞~
这里陌生的东西主要有两个。KotlinJsonAdapterFactory
和adapter
。
① KotlinJsonAdapterFactory
是生成JsonAdapter的工厂
② val adapter = moshi.adapter(People::class.java)
实际上就是传了个class type
给KotlinJsonAdapterFactory,而后工厂建立出adapter
③ adapter调用本身的fromJson
和toJson
方法完成序列化和反序列化过程。
把上面的代码翻译成流程图就是:
Moshi反序列化过程就是这么简单!
了解了Moshi的反序列化流程,咱们来回答一下上面哥哥的问题
为何说用Moshi更符合kotlin语法规范呢?
咱们先回到Moshi使用的那个例子,这个反序列化的结果是什么呢?
是 People(name="张三", age = 18) email = "helloworld@163.com"
仍是 People(name="张三", age = 18) email = ""
咱们从Kotlin语法分析,email
是 val
不可变的。序列化实际上就是建立一个对象而后根据json进行赋值,那么Moshi会对不可变的 email
进行赋值吗
答案是不会。
胆大心细脸皮厚的哥哥可能已经看到上面流程图中KotlinJsonAdapterFactory
在生成 JsonAdapter
过程当中,有一个「检验对象是否合法」的过程,我偷偷贴这个过程当中的一行源码:
// 位于:KotlinJsonAdapterFactory#create()
if (property !is KMutableProperty1 && parameter == null) continue
复制代码
哎哎哎,我不是标题党,这不是源码,是英语英语,不信我给你解释解释
propery
-- 表示数据类中全部的成员属性。用上面的例子来讲就是val name:String
,val age:Int
和 var 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
复制代码
我可没有贴代码哦,是大家本身要读的~
我是方木
不喜欢大段源码
想把技术用通俗简单的语言描述清楚
想写一些本身快乐,你们快乐的东西
欢迎各位点赞关注~~