Retrofit 返回值序列化增加 JSONObject 类型支持
如 {"msg":"success","code":0,"data":{}} 对应类为
data class Result(val code: Int = 0, val msg: String = "", val data: JSONObject = JSONObject())
让结果 data 不再为空空花括号 { }
一、Retrofit 返回值序列化,变化部分设置为 JSONObject 解决方案
部分属性使用 JSONObject 类型声明后,接收到内容值一直为空,解决方案
- 自定义解析类型(反序列化)
继承
JsonDeserializer<T>实现自己的解析,仅针对 T 这类型生效,不影响其他类 - 将该类型增加至 Gson
// XX 为你最终要的类,XXType 为第一点自定义的类 val gson = GsonBuilder() .registerTypeAdapter(XX::class.java, XXType()) .create() - 将该类添加到 GsonConverterFactory
GsonConverterFactory.create(gson)
val retrofit = Retrofit.Builder() .baseUrl("http://localhost:8080/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gson)) .build()
详细的例子可以直接拉到文章下面查阅。
二、本文需求来源
在网络请求中,通常会以固定格式返回数据,方便客户端做解析,如 {"msg":"success","code":0,"data":{}},为了解析通用,一般会使用泛型来做数据解析,如下
data class Result<T>(val code:Int,val msg:String,val data:T)
data class UserInfo(val name:String)
声明接口
interface Api{
@Get("/login")
fun login():Observable<Result<UserInfo>>
//这种是不支持,反序列化必须知道具体类型,泛型会被擦除,所以无法得知具体类型抛出异常
//fun <T> login():Observable<Result<T>>
}
具体使用
fun test{
//省略
val api = retrofit.create(Api::class.java)
api.login().subscribe({result -> println("success")},
{e -> printStackTrace()})
}
如果存在接口少,定义出各类具体的 data 实例,也不是什么大问题,无非是不同接口新建一个新类用来反序列化构建而已,但如果遇到多重嵌套,那编写的类就多了,如下情况
{"msg":"success","code":0,"data":{"pic":[{"id":1,"name":"001.jpg","url":"file/001.jpg"},{"id":2,"name":"003.jpg","url":"file/003.jpg"}]}}
这时还想使用上面的 Result<T>,那么需要新建一个子项 Item,以及 Pic 类包含 List,所以对应编写如下
data class Item(val name:String,val url:String)
data class Pic(val pic:List<Item>)
interface Api{
@Get("/getData")
fun getData():Observable<Result<Data>>
}
fun test(){
api.test()
.subscribe({ result ->
println(result.toString())
}, { e ->
e.printStackTrace()
})
}
输出
Result(code=0, msg=success, data=Pic(pic=[Item(name=001.jpg, url=file/001.jpg), Item(name=003.jpg, url=file/003.jpg)]))
kotlin 写还好,如果是 java 类数量就暴增。
那么能否使用 JSONObject 来表示 data 这个参数?简单尝试一下:
data class Result(val code:Int,val msg:String,val data:JSONObject)
interface Api{
@Get("/login")
fun login():Observable<Result>
}
fun login(){
api.login()
.subscribe({ result ->
println(result.toString())
}, { e ->
e.printStackTrace()
})
}
//输出的结果 Result(code=0, msg=success, data={})
//这个结果和你直接定义返回类型是 JSONObject 一致
//无法匹配到类型进行反序列化,得到结果一直是 {}
interface Api{
@Get("/login")
fun login():Observable<JSONObjcet>
}
以上,会有一种解决方案,直接舍弃 Result 用 JSONObject 来替代,替换 GsonConverterFactory.create() 为能解析 JSONObject 的 Converter 即可,本文不做描述。
三、具体解决示例
替换 GsonConverterFactory.create() ,这不是本文的目标,我的目的是使用通用类,让 data 类型为 JSONObject,即保留部分属性,让另一个属性(对象)以JSONObject 形式存在,测试结果很明显,Gson 不支持 JSONObject 这个类型,那么我们直接定义一个 Result 类型解析即可,如下
//通用类
data class Result(val code: Int = 0, val msg: String = "", val data: JSONObject = JSONObject())
//解析类
class JSONResultType : JsonDeserializer<Result> {
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Result {
val result = if (json.isJsonObject) {
val obj = json.asJsonObject
val code = obj.get("code").asInt
val msg = obj.get("msg").asString
val data = JSONObject(obj.getAsJsonObject("data").toString())
Result(code, msg, data)
} else {
Result()
}
return result
}
}
使用,在 Retrofit 初始化增加类型即可,如下
fun test(){
val gson = GsonBuilder()
.registerTypeAdapter(Result::class.java, JSONResultType())
.create()
val retrofit = Retrofit.Builder()
.baseUrl("http://localhost:8080/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
测试调用,输出结果如下:
==========
Result<UserInfo>============== Result(code=0, msg=success, data=UserInfo(name=zhangsan)) ==========JSONResult============== JSONResult(code=0, msg=success, data={"name":"zhangsan"})
至于为什么不直接将整个结果替换为 JSONObject,主要是懒,kotlin 一个 . 解决的事,才不想用 optXXX(),以上。
本文结束,感谢阅读