You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Moshi反序列化泛型响应 处理data字段为不同类型的服务端返回结果

Moshi完全支持该泛型适配需求,推荐使用自定义JsonAdapter.Factory的方案实现,无性能开销且无需修改原有接口定义:


步骤1:调整基础响应模型的可空性

原有非空data字段在错误响应为空对象时会直接触发解析异常,先修改为可空类型:

data class BaseResponseResult<T>(
    @Json(name = "data") val data: T?, // 修改为可空
    @Json(name = "success") val success: Boolean,
    @Json(name = "errors") val errors: List<Error>
)

步骤2:实现泛型适配的JsonAdapter工厂

通过工厂动态获取对应接口的data泛型类型解析器,同时统一处理错误抛出逻辑,适配RxJava的错误回调机制:

import com.squareup.moshi.*
import com.squareup.moshi.internal.Util
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

class BaseResponseJsonAdapterFactory : JsonAdapter.Factory {
    override fun create(
        type: Type,
        annotations: MutableSet<out Annotation>,
        moshi: Moshi
    ): JsonAdapter<*>? {
        // 仅处理BaseResponse类型的反序列化
        if (Types.getRawType(type) != BaseResponse::class.java) return null

        // 提取BaseResponse的泛型参数(即对应接口的data类型)
        val dataType = Types.getParameterUpperBound(0, (type as ParameterizedType).actualTypeArguments[0])
        val dataAdapter = moshi.adapter<Any>(dataType)
        val resultType = Types.newParameterizedType(BaseResponseResult::class.java, dataType)
        val resultAdapter = moshi.adapter<BaseResponseResult<Any>>(resultType)
        val stringAdapter = moshi.adapter(String::class.java)
        val nullableStringAdapter = stringAdapter.nullSafe()

        return object : JsonAdapter<BaseResponse<Any>>() {
            override fun fromJson(reader: JsonReader): BaseResponse<Any> {
                reader.beginObject()
                var jsonrpc = ""
                var id: String? = null
                var result: BaseResponseResult<Any>? = null
                while (reader.hasNext()) {
                    when (reader.nextName()) {
                        "jsonrpc" -> jsonrpc = stringAdapter.fromJson(reader) ?: ""
                        "id" -> id = nullableStringAdapter.fromJson(reader)
                        "result" -> result = resultAdapter.fromJson(reader)
                        else -> reader.skipValue()
                    }
                }
                reader.endObject()

                val finalResult = result ?: throw JsonDataException("响应缺失result字段")
                // 错误响应直接抛业务异常,RxJava自动进入onError回调
                if (!finalResult.success) {
                    val error = finalResult.errors.firstOrNull()
                    throw BusinessException(
                        code = error?.code ?: -1,
                        errorMsg = error?.message ?: "未知业务错误"
                    )
                }
                // 成功响应校验data非空
                val finalData = finalResult.data ?: throw JsonDataException("成功响应缺失data字段")
                return BaseResponse(jsonrpc, id, finalResult)
            }

            override fun toJson(writer: JsonWriter, value: BaseResponse<Any>?) {
                // 仅用于响应解析无需序列化,空实现即可
            }
        }
    }
}

// 自定义业务异常,统一在RxJava的onError中处理
class BusinessException(val code: Int, val errorMsg: String) : Exception(errorMsg)

步骤3:注册工厂到Moshi实例

初始化Moshi时将自定义工厂放在最前面注册即可,原有Retrofit接口、业务响应类无需任何修改:

val moshi = Moshi.Builder()
    .add(BaseResponseJsonAdapterFactory())
    .add(KotlinJsonAdapterFactory()) // 配合Kotlin数据类使用必须添加
    .build()

// 后续将该Moshi实例配置给Retrofit的MoshiConverterFactory即可

方案适配效果

  • 请求成功时自动将data解析为对应接口定义的实体类型(如LoginResponse、ProfileResponse)
  • 请求失败时自动抛出业务异常,无需在每个接口回调中重复判断success字段
  • 支持任意泛型类型的data字段扩展,新增接口无需额外修改适配逻辑

内容的提问来源于stack exchange,提问作者allo86

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 22:27:02