Retrofit自定义GsonConverter+Exception拦截json解析异常问题

最近使用到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();

经调试,没有问题,有什么我没注意到的地方欢迎提出一起探讨。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值