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

使用Gson与Retrofit处理类型标识在外层的多态JSON解析问题

解决方案1:自定义Gson TypeAdapter(最适配当前场景的方案)

该方案可以一次性完成全字段解析,后续使用无额外适配成本,实现逻辑如下:

  1. 首先定义所有screen对应data类的公共父接口:
interface ScreenData

让已定义的各个data类实现该接口:

data class PhoneData(
    @SerializedName("phone_number")
    val phoneNumber: String? = null,
    @SerializedName("phone_status")
    val phoneStatus: String? = null
) : ScreenData

data class DeepLinkData(
    val deepLink: String? = null
) : ScreenData

// 其余screen对应的data类都按此规则实现ScreenData接口
  1. 定义通用响应包装类和screen枚举(因为所有screen取值都是已知固定值):
enum class ScreenType {
    @SerializedName("phone") PHONE,
    @SerializedName("main_screen") MAIN_SCREEN,
    @SerializedName("documents_rejected") DOCUMENTS_REJECTED,
    // 补全其余所有screen枚举值
}

data class CommonResponse<T : ScreenData>(
    val result: String? = null,
    val code: Int? = null,
    val status: String? = null,
    val screen: ScreenType? = null,
    @SerializedName("next_screen")
    val nextScreen: String? = null,
    val message: String? = null,
    val data: T? = null
)
  1. 自定义TypeAdapter处理解析逻辑,优先读取外层screen字段,再根据screen值将data解析为对应类型:
class CommonResponseTypeAdapter : TypeAdapter<CommonResponse<*>>() {
    private val gson = Gson()

    override fun write(out: JsonWriter, value: CommonResponse<*>?) {
        // 有序列化需求时实现该方法,无需求可省略
        gson.toJson(value, value?.javaClass, out)
    }

    override fun read(`in`: JsonReader): CommonResponse<*> {
        val jsonElement = JsonParser.parseReader(`in`)
        val jsonObject = jsonElement.asJsonObject

        // 先解析公共字段
        val baseResponse = gson.fromJson(jsonObject, CommonResponse::class.java)
        val screenType = baseResponse.screen ?: throw IllegalArgumentException("screen字段不能为空")
        val dataJson = jsonObject.get("data")

        // 根据screen类型解析对应data结构
        val data = when (screenType) {
            ScreenType.PHONE -> gson.fromJson(dataJson, PhoneData::class.java)
            ScreenType.MAIN_SCREEN -> gson.fromJson(dataJson, DeepLinkData::class.java)
            // 补全其余screen类型和对应data类的映射
            else -> throw IllegalArgumentException("未适配的screen类型:$screenType")
        }

        return baseResponse.copy(data = data)
    }
}
  1. 将适配器注册到Gson实例即可使用:
val gson = GsonBuilder()
    .registerTypeAdapter(CommonResponse::class.java, CommonResponseTypeAdapter())
    .create()

后续解析时直接调用gson.fromJson(jsonStr, CommonResponse::class.java)即可,使用时判断screen类型后将data强转为对应类即可获取全部字段。


解决方案2:二次解析(适合screen类型少、需要快速实现的场景)

不需要自定义适配器,先解析公共字段,再手动解析data:

// 定义中间响应类,data先存为原始JsonElement
data class TempCommonResponse(
    val result: String? = null,
    val code: Int? = null,
    val status: String? = null,
    val screen: ScreenType? = null,
    @SerializedName("next_screen")
    val nextScreen: String? = null,
    val message: String? = null,
    val data: JsonElement? = null
)

// 解析逻辑
val tempResponse = gson.fromJson(jsonStr, TempCommonResponse::class.java)
val screenData = when(tempResponse.screen) {
    ScreenType.PHONE -> gson.fromJson(tempResponse.data, PhoneData::class.java)
    ScreenType.MAIN_SCREEN -> gson.fromJson(tempResponse.data, DeepLinkData::class.java)
    // 补全其余类型映射
    else -> null
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 20:24:04