Moshi是什么
一句話描述
Moshi就是一個實(shí)現(xiàn)了對Json序列化和反序列化的開源庫
大名鼎鼎的Gson大家肯定都知道,Moshi本質(zhì)上就是干了和他一樣的事
( 本文是建立在對Moshi使用有一些簡單了解的基礎(chǔ)上赌躺,如果還沒有了解過Moshi狼牺,建議看下這篇文章新一代Json解析庫Moshi使用及原理解析)
為什么使用Moshi
一句話描述
Gson是一個針對Java的Json序列化和反序列化庫
Moshi是一個針對Kotlin的Json序列化和反序列化庫
在各種更高級語言涌現(xiàn)的今天,特別是kotlin的誕生礼患,雖然本質(zhì)上還是java那一套是钥,但各種方便易用的api和語法糖,讓人大呼爽歪歪缅叠。而老牌的Gson在處理Kotlin序列化問題上顯得有些力不從心悄泥,主要有兩個問題:
(1)空安全問題
Kotlin對變量分為可空類型和不可空類型,而Java中基本上所有變量都是可空的(除了@NonNullable標(biāo)簽)肤粱。如果Json中一個變量值是null弹囚,但Kotlin聲明這個變量是非空,Gson仍然會把這個變量賦值為null
(2)默認(rèn)參數(shù)失效
默認(rèn)參數(shù)是Kotlin的一個新語法领曼。是在定義一個data類時鸥鹉,直接在構(gòu)造函數(shù)中賦給參數(shù)默認(rèn)值蛮穿。例如:
class People(val name:String, val age:Int = 18)復(fù)制代碼
當(dāng)Json是:
{? ? "name" : "哈哈哈"}復(fù)制代碼
根據(jù)Kotlin的語法,期望得到的對象是
People(name="哈哈哈"宋舷,age = 18)復(fù)制代碼
但實(shí)際上Gson會解析成
People(name="哈哈哈"绪撵,age = 0)復(fù)制代碼
這樣就造成默認(rèn)參數(shù)age = 18的丟失
為了讓Json解析更加符合kotlin的語法規(guī)范,于是選擇使用Moshi
Moshi是如何實(shí)現(xiàn)反序列化的
這時候好奇的哥哥可能就要問了
為什么Moshi的解析更符合Kotlin的語法規(guī)范呢祝蝠? 你說符合就符合啊幻碱?
別急別急绎狭,想弄清這個問題,需要了解一下Moshi是如何實(shí)現(xiàn)反序列化的
我們先看一下Moshi反序列化的簡單使用:
// 需要反序列化的jsonval json = "{? ? "name": "張三",? ? "age": 18,? ? "email": "helloword@163.com"}"http:// 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}復(fù)制代碼
( Model類里藏了一個坑儡嘶,后面揭曉 ^ ^ )
沒有用過Moshi的哥哥可能很反感這種行為,“來不來就給我整幾行代碼恍风?我是看了代碼就能記住的人嗎蹦狂?!”
別急別急朋贬,我這就一點(diǎn)一點(diǎn)給您掰開了揉碎了說凯楔,您就舒舒服服躺好嘞~
這里陌生的東西主要有兩個。KotlinJsonAdapterFactory和adapter锦募。
①KotlinJsonAdapterFactory 是生成JsonAdapter的工廠
② val adapter = moshi.adapter(People::class.java)實(shí)際上就是傳了個class type給KotlinJsonAdapterFactory摆屯,然后工廠創(chuàng)建出adapter
③ adapter調(diào)用自己的fromJson和toJson方法完成序列化和反序列化過程。
把上面的代碼翻譯成流程圖就是:
Moshi反序列化過程就是這么簡單糠亩!
校驗(yàn)對象
了解了Moshi的反序列化流程虐骑,我們來回答一下上面哥哥的問題
為什么說用Moshi更符合kotlin語法規(guī)范呢?
我們先回到Moshi使用的那個例子赎线,這個反序列化的結(jié)果是什么呢廷没?
是People(name="張三", age = 18) email = "helloworld@163.com"
還是People(name="張三", age = 18) email = ""
我們從Kotlin語法分析,email是val不可變的垂寥。序列化實(shí)際上就是創(chuàng)建一個對象然后根據(jù)json進(jìn)行賦值颠黎,那么Moshi會對不可變的email進(jìn)行賦值嗎
答案是不會。
膽大心細(xì)臉皮厚的哥哥可能已經(jīng)看到上面流程圖中KotlinJsonAdapterFactory在生成JsonAdapter過程中矫废,有一個「檢驗(yàn)對象是否合法」的過程盏缤,我偷偷貼這個過程中的一行源碼:
// 位于:KotlinJsonAdapterFactory#create() if (property !is KMutableProperty1 && parameter == null) continue復(fù)制代碼
哎哎哎,我不是標(biāo)題黨蓖扑,這不是源碼唉铜,是英語英語,不信我給你解釋解釋
propery-- 表示數(shù)據(jù)類中所有的成員屬性律杠。用上面的例子來說就是val name:String潭流,val age:Int和var email : String = ""
KMutableProperty1-- 是Kotlin中的類型對象竞惋。表示的是var這個可變類型
parameter-- 表示構(gòu)造函數(shù)中的參數(shù)。用上面的例子就是val name:String灰嫉,val age:Int(這里聯(lián)系上下文知道parameter == null是用來判斷property是不是構(gòu)造函數(shù)中聲明的成員屬性)
continue-- 跳過拆宛。不對這個屬性反序列化了
所以用偉大中華人民共和國56個民族之一的漢族語言來說就是:
如果一個成員屬性,他是不可變的(也就是val)并且不是構(gòu)造函數(shù)中聲明的讼撒,就跳過它
看到了吧浑厚,KotlinAdapterJsonFactory實(shí)際上就是區(qū)別于Gson的關(guān)鍵之一,它對類對象做了一些校驗(yàn)根盒,使Moshi更加符合Kotlin語法
真正實(shí)現(xiàn)
KotlinJsonAdapter是Moshi的另一個核心钳幅,是Model的序列化和反序列化的真正實(shí)現(xiàn)
adapter的核心是binding
這里我們可以類比一下Model。一個Model對應(yīng)一個adapter炎滞,Model中的成員屬性就是一個個Binding
Binding負(fù)責(zé)基本數(shù)據(jù)類型的解析敢艰。IntBinding里面有個IntAdapter,負(fù)責(zé)解析json中的int類型册赛。StringBinding里有個StringAdapter钠导,負(fù)責(zé)解析json中的string類型。
總的來說森瘪,Binding就是負(fù)責(zé)拿到j(luò)son中字段的類型和值牡属,然后自己保存起來
至于json中字段的類型和值是如何被識別的嘛,Moshi基本上就是抄Gson的JsonReader和JsonWriter
KotlinJsonAdapter通過反射生成一個類對象柜砾,這些binding把成員屬性的值一個個賦值到屬性上湃望,最終生產(chǎn)出一個model對象。
等等等等痰驱,通過反射生成對象证芭?咋這么抽象呢?
很簡單啦担映。其實(shí)就是調(diào)用Android的Constructor類中newInstance(*args)方法
只要有類對象废士,生成實(shí)例對象就是一句話的事
People::class.java.constructors[1].newInstance("張三", 20)// constructors[0]是參數(shù)最多的構(gòu)造函數(shù)// 第三個參數(shù)表示第幾個構(gòu)造參數(shù)屬性用默認(rèn)參數(shù)的值// 所以結(jié)果是:People("張三", 18)People::class.java.constructors[0].newInstance("張三", 20, 2, null)復(fù)制代碼
到目前為止,我們基本上順了一遍Moshi反序列化的過程蝇完。盡量不貼大段代碼官硝,是不希望大家在好不容易啃完一遍源碼之后,過幾個星期就感覺似是而非短蜕,似懂非懂
所以我希望能給大家一個簡單的體系氢架, 有一些不得不用源碼的地方也是盡量給出通俗的解釋。
代碼只是細(xì)節(jié)的實(shí)現(xiàn)朋魔,是知識體系上的枝葉岖研。如果大家想再深入了解某些細(xì)節(jié),可以閱讀以下方法
// factory生成adapterKotlinJsonAdapterFactory#create()// adapter序列化和反序列化:KotlinJsonAdapter#fromJsonKotlinJsonAdapter#toJson// 反射生成對象KCallableImpl#callBy#callDefaultMethodCallerImpl>Constructor#callConstructor#newInstance復(fù)制代碼