Kotlin Android中GSON序列化重写属性报错求助
问题分析与解决方案
首先,咱们来拆解你遇到的问题:你想用子类MetaInner重写父类响应中的Meta属性,并且解析同一个SerializedName,但Retrofit(准确说是Gson转换器)报错无法创建转换器。这背后的核心原因是Gson默认无法自动处理继承体系下的属性类型协变反序列化——虽然Kotlin允许你用子类类型重写父类的属性,但Gson在解析时,还是会按照父类Meta的类型去实例化对象,然后试图把它赋值给子类类型的meta属性,这就导致了类型不匹配,最终抛出IllegalArgumentException。
接下来给你两种在Kotlin中实现需求的可行方案,你可以根据自己的场景选择:
方案一:使用泛型基类(最简洁推荐)
把你的基类响应改成泛型类,让每个子类响应可以指定自己的Meta子类类型,这样Gson能明确知道要解析的meta字段类型:
// 基类响应,用泛型指定Meta类型 open class BaseResponse<M : Meta>( @SerializedName("meta") open val meta: M ) // 基础Meta类 open class Meta { @SerializedName("code") val code: Int = 0 } // 扩展的Meta子类 class MetaInner : Meta() { @SerializedName("extra_field") val extraField: String? = null } // 具体的响应类,指定Meta类型为MetaInner class SomeResponse( @SerializedName("meta") override val meta: MetaInner ) : BaseResponse<MetaInner>(meta)
这样定义后,Retrofit在处理SomeResponse时,Gson会明确知道meta字段需要解析成MetaInner类型,不会再出现类型不匹配的问题。
方案二:自定义Gson TypeAdapter(适合无法修改基类的场景)
如果你不能修改基类BaseResponse的结构,可以给Gson注册一个自定义的TypeAdapterFactory,专门处理SomeResponse的meta字段解析:
第一步:编写自定义TypeAdapterFactory
class ResponseTypeAdapterFactory : TypeAdapterFactory { override fun <T> create(gson: Gson, typeToken: TypeToken<T>): TypeAdapter<T>? { // 只处理BaseResponse的子类 if (!BaseResponse::class.java.isAssignableFrom(typeToken.rawType)) { return null } // 针对SomeResponse做特殊处理 if (typeToken.rawType == SomeResponse::class.java) { // 获取默认的解析适配器 val delegateAdapter = gson.getDelegateAdapter(this, TypeToken.get(SomeResponse::class.java)) // 获取MetaInner的解析适配器 val metaInnerAdapter = gson.getAdapter(MetaInner::class.java) return object : TypeAdapter<SomeResponse>() { override fun write(writer: JsonWriter, value: SomeResponse) { delegateAdapter.write(writer, value) } override fun read(reader: JsonReader): SomeResponse { // 先把整个响应解析成JsonObject val jsonObject = JsonParser.parseReader(reader).asJsonObject // 单独把meta字段解析成MetaInner val meta = metaInnerAdapter.fromJsonTree(jsonObject.get("meta")) // 解析整个响应对象 val response = delegateAdapter.fromJsonTree(jsonObject) // 替换meta属性(注意这里meta需要是var类型,如果是val的话要在构造时处理) response.meta = meta return response } } as TypeAdapter<T> } return null } }
第二步:注册到Gson并配置Retrofit
// 创建带自定义适配器的Gson实例 val customGson = GsonBuilder() .registerTypeAdapterFactory(ResponseTypeAdapterFactory()) .create() // 配置Retrofit时使用这个Gson val retrofit = Retrofit.Builder() .baseUrl("你的BaseUrl") .addConverterFactory(GsonConverterFactory.create(customGson)) .build()
额外注意点
- 如果你的
meta属性是val(不可变),那么在方案二中,你需要修改SomeResponse的构造函数,让它能接收MetaInner作为参数,而不是直接替换属性值。 - 如果你用的是其他JSON转换器(比如Moshi),思路类似:需要告诉转换器在解析特定响应类时,把
meta字段解析成子类类型。
内容的提问来源于stack exchange,提问作者Wackaloon
相关产品推荐
相关产品推荐

