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
相关产品推荐
相关产品推荐

