使用Gson与Retrofit处理类型标识在外层的多态JSON解析问题
解决方案1:自定义Gson TypeAdapter(最适配当前场景的方案)
该方案可以一次性完成全字段解析,后续使用无额外适配成本,实现逻辑如下:
- 首先定义所有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接口
- 定义通用响应包装类和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 )
- 自定义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) } }
- 将适配器注册到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
相关产品推荐
相关产品推荐

