別給我講源碼,告訴我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
複製代碼

我可沒有貼代碼哦,是大家本身要讀的~

我是方木

不喜歡大段源碼

想把技術用通俗簡單的語言描述清楚

想寫一些本身快樂,你們快樂的東西

歡迎各位點贊關注~~

相關文章
相關標籤/搜索