最近使用到Retrofit的请求对服务器数据反序列化,出现的问题是:
服务器数据返回成功如下:{“status”:1,”msg”:”请求成功”,”result”:{……}}
失败如下:{“status”:-1,”msg”:”请求失败”,”result”:”“}
反序列化的问题就是配置相应的结果bean的时候,如果设置Result为String类型,成功转型失败,而且就算成功里边结果信息也没有转化,如果设置ResultBean类型的,失败就还是会抛出转型期望失败,一般上来说我们是设置好带ResultBean的双层Bean映射的,那么如何处理这个问题,想到的对策就是请求之前拦截一下,先自定义忽略解析result字段然后如果返回码为1代表成功,则解析后续的resultbean,否则就直接抛出自定义异常,也就是忽略解析的bean,内部只包含status和msg的信息,做相应的处理即可。
自定义Convert类这里我们只用到的response返回的处理所以只处理response的即可,经查看GsonConverterFactory可知道这个请求有请求Request和响应Response两个关联类:
顾名思义,请求前处理和响应后处理,因此ConverterFactory的代码和请求类Request的代码都不必动,只在Response类中自定义处理自己的逻辑即可:
CustomGsonConverterFactory.kt(未修改)
/**
* Created by Panda on 2017/11/18.
* 自定义工厂类 内容和原始一样 只修改CustomGsonResponseBodyConverter类即可
*/
class CustomGsonConverterFactory private constructor(private val gson: Gson?) : Converter.Factory() {
init {
if (gson == null) throw NullPointerException("gson == null")
}
override fun responseBodyConverter(type: Type?,annotations: Array<Annotation>?, retrofit: Retrofit?): CustomGsonResponseBodyConverter<out Any> {
val adapter = gson!!.getAdapter(TypeToken.get(type!!))
return CustomGsonResponseBodyConverter(gson, adapter as TypeAdapter<*>)
}
override fun requestBodyConverter(type: Type?, parameterAnnotations: Array<Annotation>?, methodAnnotations: Array<Annotation>?, retrofit: Retrofit?): Converter<*, RequestBody>? {
val adapter = gson!!.getAdapter(TypeToken.get(type!!))
return CustomGsonRequestBodyConverter(gson, adapter as TypeAdapter<*>)
}
companion object {
@JvmOverloads
fun create(gson: Gson = Gson()): CustomGsonConverterFactory {
return CustomGsonConverterFactory(gson)
}
}
}
CustomGsonRequesteBodyConverter.kt(未修改)
/**
* Created by Panda on 2017/11/18.
* 自定义请求类 内容和原始一样 只修改CustomGsonResponseBodyConverter类即可
*/
internal class CustomGsonRequestBodyConverter<T>(private val gson: Gson, private val adapter: TypeAdapter<T>) : Converter<T, RequestBody> {
@Throws(IOException::class)
override fun convert(value: T): RequestBody {
val buffer = Buffer()
val writer = OutputStreamWriter(buffer.outputStream(), UTF_8)
val jsonWriter = gson.newJsonWriter(writer)
adapter.write(jsonWriter, value)
jsonWriter.close()
return RequestBody.create(MEDIA_TYPE, buffer.readByteString())
}
companion object {
private val MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8")
private val UTF_8 = Charset.forName("UTF-8")
}
}
CustomGsonResponseBodyConverter.kt(自定义修改)
/**
* Created by Panda on 2017/11/18.
* 自定义响应类 修改做拦截抛出操作 应用到Gson反序列和序列化部分的知识
*/
class CustomGsonResponseBodyConverter<T>(private val gson: Gson, private val adapter: TypeAdapter<T>) : Converter<ResponseBody, T> {
@Throws(IOException::class)
override fun convert(value: ResponseBody): T {
val response = value.string()
val httpStatus = gson.fromJson(response, CustomStatus::class.java)
//验证status返回是否为1
if (httpStatus.isRequestSuccess) {
value.close()
//不为-1,表示响应数据不正确,抛出异常
throw CustomException(httpStatus.status, httpStatus.msg.toString())
}
//继续处理body数据反序列化,注意value.string() 不可重复使用
val contentType = value.contentType()
val charset = if (contentType != null) contentType.charset(UTF_8) else UTF_8
val inputStream = ByteArrayInputStream(response.toByteArray())
val reader = InputStreamReader(inputStream, charset!!)
val jsonReader = gson.newJsonReader(reader)
try {
return adapter.read(jsonReader)
} finally {
value.close()
}
}
}
CustomException.kt
data class CustomException(val mErrorCode: Int, val errorMessage: String) : RuntimeException(errorMessage)
CustomStatus.kt
class CustomStatus : Serializable {
val status: Int = 0
val msg: String? = null
//不参与序列化和反序列化
@Transient private val result: String? = null
val isRequestSuccess: Boolean
get() = status != 1
}
这里我都转换成kotlin的代码来配置了,转换过程中还是有一些语法上的错误的,最后转换调试应该没什么问题,如果有什么问题欢迎反应联系。@Transient修饰的变量不参与序列化和反序列化,data class专门又来处理数据展示类,比java简洁的是默认实现了getter和setter方法,一行代码解决java一个类,好多data class是可以放在同一个kt文件下,方便统一管理。
好了自定义完后了,最后在retrofit的调用处加上自己的gson解析器就是了:(注意kotlin的companion的使用)
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(BASE_URL)
.addConverterFactory(CustomGsonConverterFactory.Companion.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
经调试,没有问题,有什么我没注意到的地方欢迎提出一起探讨。