Kotlin Gson RuntimeTypeAdapterFactory
Kotlin
gson
convert
multiplier type
RuntimeTypeAdapterFactory
gson
gson 是一個 android 開發者很常使用的 Json 轉換套件, 是 google 開發的
在一般公司來說 Server Api Call return 的內容裡面,總是會出現類似下列這樣的 JSON 內容格式
{
"data": [
{
"type": "title",
"name": "..."
},
{
"type": "first",
"data": [
{
"title": "",
"description": ""
}
]
},
{
"type": "second",
"data": [
{
"image_url": "https://xxx.xxx.xx/xx.jpg",
"url": "https://xx.xxx.xx/xx/xx"
}
]
}
]
}
Array 裡面的的每一筆並不是一樣的格式
看到開始想的是,應該用什麼樣的 Model 去把些內容接下來,想不出來,就去跟後端說,這樣的格式不合理
後端把 前端 iOS 都找來問,前端 跟 iOS 都說他們可以接
那只好再想想辦法….
RuntimeTypeAdapterFactory 就是你需要的, 你可以在 gson 的 repo 裡面看到他
可是你若是在 Gradle 裡面,只有
implementation 'com.google.code.gson:gson:2.8.5'
是無法使用到的
他是被放在
implementation 'org.danilopianini:gson-extras:0.2.1'
使用的方式基本上如下 (以上方的 JSON 為解析對象)
建立相對應的 Model, 記得要繼承同一個,這裡用的是 BaseTestModel
open class BaseTestModel(val type: String)
data class FirstModel(val title: String, val description: String)
data class SecondModel(val image_url: String, val url: String)
class TestTitleModel(type: String, name: String) : BaseTestModel(type)
class TestFirstModel(type: String, data: List<FirstModel>) : BaseTestModel(type)
class TestSecondModel(type: String, data: List<SecondModel>) : BaseTestModel(type)
建立一個 convertFactory
private object TestResultFactory {
private val baseAdapterFactory = RuntimeTypeAdapterFactory.of(BaseTestModel::class.java)
.registerSubtype(TestTitleModel::class.java, "title")
.registerSubtype(TestFirstModel::class.java, "first")
.registerSubtype(TestSecondModel::class.java, "second")
private val gson = GsonBuilder().registerTypeAdapterFactory(baseAdapterFactory).create()
fun create(jsonElement: JsonElement?): BaseTestModel? {
return gson.fromJson<BaseTestModel?>(jsonElement, BaseTestModel::class.java)
}
}
建立 SubClass of JsonDeserializer
class TestDataDeserializer : JsonDeserializer<BaseTestModel> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): BaseTestModel? {
return TestResultFactory.create(json)
}
}
放在 gson Build 裡面去註冊
GsonBuilder()
.registerTypeAdapter(BaseTestModel::class.java, TestDataDeserializer())
.create()
搞定, 可以順利把整個都正確的接下來了
當然取用的時候,記得要轉型,才可以取得每一個型別不同的內容
看來一切如此美好,自然就上線了
某天,後端給的新的一個 type “main”, app 顯示一片空白
千追萬追才發現,RuntimeTypeAdapterFactory 處理到沒有註冊的 type 就會 throw error
要怎樣避免加了一個新的 type 就讓整個解析失敗呢
那就不要傳入沒有註冊的 type 去解析
簡單說就是對 TestResultFactory 動一點手腳
private object TestResultFactory {
private val supportTypes: List<String> = listOf("title", "first", "second")
private val baseAdapterFactory = RuntimeTypeAdapterFactory.of(BaseTestModel::class.java)
.registerSubtype(TestTitleModel::class.java, "title")
.registerSubtype(TestFirstModel::class.java, "first")
.registerSubtype(TestSecondModel::class.java, "second")
private val gson = GsonBuilder().registerTypeAdapterFactory(baseAdapterFactory).create()
fun create(jsonElement: JsonElement?): BaseTestModel? {
// 修改在這裡,判斷如果是 support 的 type 才去解析,否則直接 return Base Type
return jsonElement?.asJsonObject?.deepCopy()?.remove("type")?.asString?.takeIf {
it.isNotEmpty() && supportTypes.contains(it)
}?.let {
gson.fromJson<BaseTestModel?>(jsonElement, BaseTestModel::class.java)
} ?: BaseTestModel("")
}
}
記得要解析完,要排除基本的 type,因為那是你沒有 Support 的 Type
打完收工