1. 程式人生 > 其它 >對json用Gson進行自定義解析

對json用Gson進行自定義解析

一:面對的資料

{
    "code": 0,
    "key": "common_success",
    "msg": "成功",
    "data": {
        "ver": "1",
        "items": [
            {
                "name": "glt"
            },
            {
                "data": "229e3b64920042109cf294f3773b6837",
                "name": "rtck"
            },
        {
                "data": 1,
                "name": "tuio"
            },
            {
                "data": {
                    "sizeLimit": 30,
                    "interval": 172800000,
                    "url": "https://dldir1.qq.com/weixin/Windows/WeChatSetup.exe"
                },
                "name": "anchor_netspeedcheck"
            },
            {
                
"data": { "ver": "1.0.0", "pkgname": "安卓-AsChat", "always_show": "false", "force": "false", "upgradeContent": "" }, "name": "upgrade" } ] } }

可以看到,後端給回來的Json資料是不規則的,在items

這個數組裡,元素的name欄位是string,但data欄位有的是string,有的是int,有的是類。這個時候,給Retrofit配置的Gson轉換器已經不適用了。

二:定義資料結構

// 介面的標準格式
data class Response<T>(
val code: Int,
val data: T?,
val key: String,
val msg: String
)

// Retrofit返回的資料結構
@GET(ApiUrls.GetAppConfig)
suspend fun getAppConfig(@Query("ver") ver: Int): Response<ConfigList>
data class ConfigList(
var items: List<ConfigItemBase>,
val ver: String
)
// 父類
open class ConfigItemBase() {
    var name: String = ""
}

//各個子類
class ConfigItemStrStr: ConfigItemBase() {
    var data: String = ""
}
class ConfigItemStrInt: ConfigItemBase() {
    var data: Int = 0
}
class ConfigItemStrSpeedObject: ConfigItemBase() {
    var data: SpeedCheckObject = SpeedCheckObject(0,0,"")
}
class ConfigItemStrUpgradeObject: ConfigItemBase() {
    var data: UpgradeObject = UpgradeObject("", "", "", "", "")
}



data class SpeedCheckObject(
    val interval: Int,
    val sizeLimit: Int,
    val url: String
)
data class UpgradeObject(
    val always_show: String,
    val force: String,
    val pkgname: String,
    val upgradeContent: String,
    val ver: String
)

總之,就是把資料結構定義好。因為有不同型別的資料,但他們又有共同的欄位,所以我就定義了一個父類,(如果沒有相同點的話就用共同的父類Any).

三:具體的解析過程

class AppConfigDeserializer: JsonDeserializer<ConfigList> {
    override fun deserialize(
        json: JsonElement?,
        typeOfT: Type?,
        context: JsonDeserializationContext?
    ): ConfigList {
        try {
            val gson = Gson()
            // 總的json物件
            val jsonObj = json!!.asJsonObject
            // 先建一個空列表
            val itemList = mutableListOf<ConfigItemBase>()
            // 取出json中的列表
            val jsonArray = jsonObj["items"].asJsonArray
            // 迴圈
            for (item in jsonArray) {
                try {
                    val tempObj = item.asJsonObject
                    if (tempObj["name"].asString == "translate_type") {
                        itemList.add(gson.fromJson(item, ConfigItemStrInt::class.java))
                    }else if (tempObj["name"].asString == "anchor_netspeedcheck") {
                        itemList.add(gson.fromJson(item, ConfigItemStrSpeedObject::class.java))
                    }else if (tempObj["name"].asString == "upgrade") {
                        itemList.add(gson.fromJson(item, ConfigItemStrUpgradeObject::class.java))
                    }else {
                        itemList.add(gson.fromJson(item, ConfigItemStrStr::class.java))
                    }
                }catch (e: Exception) {
                    // NOTE 有item解析錯誤,說明後臺加了新的實體,直接跳過解析錯誤的
                    LogUtil.e(item.toString())
                }
            }
            return ConfigList(itemList, jsonObj["ver"].asString)
        } catch (e: Exception) {
            // 能來到這裡的報錯,說明最外層data為”“
            return ConfigList(emptyList(), "0")
        }
    }
}

重點是認識json的幾個方法,如果熟悉這幾個方法的話就很容易了。

  1. jsonObj["items"].asJsonArray,因為我們的items欄位是一個列表,所以我們把它當一個數組對待。
  2. 對於每一個元素,用asJsonObject把它轉成一個物件。
  3. 用["xxx"]的方式取出欄位的值。
  4. 用gson對判斷出來的型別進行一個解析放進數組裡。

四:配置Retrofit

return Retrofit.Builder()
            .baseUrl(ApiUrls.BASE_URL)
            .client(client)
            .addConverterFactory(
                GsonConverterFactory.create(
                    GsonBuilder()
                        .registerTypeAdapter(ConfigList::class.java, AppConfigDeserializer())// 要自定義解析的類,自定義的解析器
                        .create()
                )
            )
            .build()