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

Android Kotlin中GSON多API响应类型反序列化优化方案咨询

针对多API响应格式的GSON反序列化优化方案

既然你已经能识别当前请求的是哪个API,除了when分支判断,这里有几个更优雅的GSON序列化方案:

1. 自定义TypeAdapter结合API标识

先针对统一业务逻辑定义抽象基类,让两个数据类继承它,再写一个自定义TypeAdapter,根据你已有的API标识(比如请求标记、URL特征)选择反序列化的具体类型:

// 抽象基类,统一核心业务字段
abstract class AuthResponse {
    abstract val accessToken: String
    abstract val expires: Long
}

// 对应第一个API的数据类
data class LegacyAuthResponse(
    @SerializedName("token") override val accessToken: String,
    @SerializedName("expires") val expiresStr: String,
    val username: String,
    val password: String
) : AuthResponse() {
    // 转换字符串时间戳为Long类型
    override val expires: Long get() = expiresStr.toLong()
}

// 对应OAuth标准的数据类
data class OAuthAuthResponse(
    @SerializedName("access_token") override val accessToken: String,
    @SerializedName("refresh_token") val refreshToken: String,
    @SerializedName("expires_in") val expiresIn: Int
) : AuthResponse() {
    // 转换相对过期时间为绝对时间戳
    override val expires: Long get() = System.currentTimeMillis() + expiresIn * 1000L
}

// 自定义TypeAdapter,根据API类型选择反序列化逻辑
class AuthResponseTypeAdapter(private val apiType: ApiType) : TypeAdapter<AuthResponse>() {
    enum class ApiType { LEGACY, OAUTH }

    override fun write(out: JsonWriter, value: AuthResponse?) {
        val gson = Gson()
        when (value) {
            is LegacyAuthResponse -> gson.toJson(value, LegacyAuthResponse::class.java, out)
            is OAuthAuthResponse -> gson.toJson(value, OAuthAuthResponse::class.java, out)
            else -> throw IllegalArgumentException("未知响应类型")
        }
    }

    override fun read(`in`: JsonReader): AuthResponse {
        val gson = Gson()
        return when (apiType) {
            ApiType.LEGACY -> gson.fromJson(`in`, LegacyAuthResponse::class.java)
            ApiType.OAUTH -> gson.fromJson(`in`, OAuthAuthResponse::class.java)
        }
    }
}

使用时根据当前API类型传入对应枚举,直接反序列化为基类,后续业务逻辑统一基于基类处理:

val apiType = if (isLegacyApi) AuthResponseTypeAdapter.ApiType.LEGACY else AuthResponseTypeAdapter.ApiType.OAUTH
val gson = GsonBuilder()
    .registerTypeAdapter(AuthResponse::class.java, AuthResponseTypeAdapter(apiType))
    .create()
val authResponse: AuthResponse = gson.fromJson(jsonString, AuthResponse::class.java)

2. 统一数据类+SerializedName别名(适合字段差异小的场景)

如果两个API的核心字段可以映射到同一数据类,直接用SerializedName的alternate属性兼容多字段名,同时处理字段格式差异:

data class UnifiedAuthResponse(
    @SerializedName(value = "token", alternate = ["access_token"]) val accessToken: String,
    @SerializedName("expires") private val expiresStr: String? = null,
    @SerializedName("expires_in") private val expiresIn: Int? = null,
    val username: String? = null,
    val password: String? = null,
    val refresh_token: String? = null
) {
    // 统一计算过期时间
    val expires: Long
        get() = when {
            expiresIn != null -> System.currentTimeMillis() + expiresIn * 1000L
            expiresStr != null -> expiresStr.toLong()
            else -> throw IllegalArgumentException("未找到过期时间字段")
        }
}

这种方式无需额外适配器,直接用Gson默认反序列化即可,缺点是会存在可空字段,业务逻辑中需要注意判空。

3. TypeAdapterFactory动态适配(适合无法提前识别API类型的场景)

如果需要从响应JSON本身判断类型(比如检查是否存在access_token字段),可以用TypeAdapterFactory动态选择反序列化逻辑,不过你已经能提前识别API类型,这个方案仅作参考:

class AuthResponseFactory : TypeAdapterFactory {
    override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
        if (type.rawType != AuthResponse::class.java) return null

        val legacyAdapter = gson.getAdapter(LegacyAuthResponse::class.java)
        val oAuthAdapter = gson.getAdapter(OAuthAuthResponse::class.java)

        return object : TypeAdapter<T>() {
            override fun write(out: JsonWriter, value: T?) {
                when (value) {
                    is LegacyAuthResponse -> legacyAdapter.write(out, value)
                    is OAuthAuthResponse -> oAuthAdapter.write(out, value)
                    else -> throw IllegalArgumentException("未知类型")
                }
            }

            override fun read(`in`: JsonReader): T {
                val jsonElement = JsonParser.parseReader(`in`)
                val jsonObject = jsonElement.asJsonObject
                return if (jsonObject.has("access_token")) {
                    oAuthAdapter.fromJsonTree(jsonElement) as T
                } else {
                    legacyAdapter.fromJsonTree(jsonElement) as T
                }
            }
        }
    }
}

注册使用:

val gson = GsonBuilder()
    .registerTypeAdapterFactory(AuthResponseFactory())
    .create()
val authResponse: AuthResponse = gson.fromJson(jsonString, AuthResponse::class.java)

方案选择建议

  • 若需严格区分响应结构、后续业务逻辑有差异,优先选自定义TypeAdapter结合API标识,逻辑清晰且类型安全。
  • 若核心字段可统一、差异较小,统一数据类+SerializedName别名是最简洁的方案。
  • 仅当无法提前识别API类型时,再考虑TypeAdapterFactory。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 23:34:55