一:面对的数据
{
"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的几个方法,如果熟悉这几个方法的话就很容易了。
- jsonObj["items"].asJsonArray,因为我们的items字段是一个列表,所以我们把它当一个数组对待。
- 对于每一个元素,用asJsonObject把它转成一个对象。
- 用["xxx"]的方式取出字段的值。
- 用gson对判断出来的类型进行一个解析放进数组里。
四:配置Retrofit
return Retrofit.Builder()
.baseUrl(ApiUrls.BASE_URL)
.client(client)
.addConverterFactory(
GsonConverterFactory.create(
GsonBuilder()
.registerTypeAdapter(ConfigList::class.java, AppConfigDeserializer())// 要自定义解析的类,自定义的解析器
.create()
)
)
.build()