如何在Kotlin中使用GSON实现多态列表反序列化?
解决Gson+Retrofit反序列化多类型列表的问题
这个场景我之前也踩过坑!Gson默认确实没法直接实例化抽象父类,因为它不知道该创建哪个具体子类的实例。下面给你一套可落地的解决方案,分步骤来:
第一步:定义数据类结构
先把三种类型的共享属性抽成抽象父类,三个子类各自实现特有属性:
// 抽象父类,包含所有类型共享的属性 abstract class BaseItem( val id: String, val name: String ) // 类型A的子类,特有属性:aField data class TypeA( id: String, name: String, val aField: Int ) : BaseItem(id, name) // 类型B的子类,特有属性:bField data class TypeB( id: String, name: String, val bField: String ) : BaseItem(id, name) // 类型C的子类,特有属性:cField data class TypeC( id: String, name: String, val cField: Boolean ) : BaseItem(id, name)
第二步:配置Gson的类型识别逻辑
这里有两种常用方案,选哪种取决于你的数据源是否有明确的类型标识字段:
方案1:用RuntimeTypeAdapterFactory(推荐,简洁高效)
Gson的扩展库提供了这个工厂类,可以根据指定字段自动匹配子类。首先需要添加依赖(以Gradle Kotlin DSL为例):
implementation("com.google.code.gson:gson-extras:2.10.1")
然后创建Gson实例时注册这个工厂,假设你的数据源返回的JSON里有type字段(值为"A"/"B"/"C")用来标识类型:
// 创建类型适配器工厂,绑定父类和类型字段 val typeAdapterFactory = RuntimeTypeAdapterFactory.of(BaseItem::class.java, "type") .registerSubtype(TypeA::class.java, "A") .registerSubtype(TypeB::class.java, "B") .registerSubtype(TypeC::class.java, "C") // 构建带类型识别能力的Gson实例 val gson = GsonBuilder() .registerTypeAdapterFactory(typeAdapterFactory) .create()
方案2:自定义TypeAdapter(灵活,适合无明确类型字段的场景)
如果你的数据源没有专门的type字段,只能通过特有属性判断(比如是否存在aField),就自己写适配器:
class BaseItemTypeAdapter : TypeAdapter<BaseItem>() { override fun write(out: JsonWriter, value: BaseItem?) { // 序列化逻辑,若不需要序列化可忽略,或根据子类类型写入对应字段 val gson = Gson() when (value) { is TypeA -> gson.toJson(value, TypeA::class.java, out) is TypeB -> gson.toJson(value, TypeB::class.java, out) is TypeC -> gson.toJson(value, TypeC::class.java, out) else -> throw IllegalArgumentException("Unknown item type: ${value?.javaClass}") } } override fun read(`in`: JsonReader): BaseItem { // 先解析成JsonObject,再根据特有属性判断类型 val jsonObject = JsonParser.parseReader(`in`).asJsonObject return when { jsonObject.has("aField") -> Gson().fromJson(jsonObject, TypeA::class.java) jsonObject.has("bField") -> Gson().fromJson(jsonObject, TypeB::class.java) jsonObject.has("cField") -> Gson().fromJson(jsonObject, TypeC::class.java) else -> throw IllegalArgumentException("Unrecognized item type") } } }
注册到Gson的方式:
val gson = GsonBuilder() .registerTypeAdapter(BaseItem::class.java, BaseItemTypeAdapter()) .create()
第三步:把配置好的Gson传给Retrofit
最后在构建Retrofit实例时,用我们自定义的Gson替换默认实现:
val retrofit = Retrofit.Builder() .baseUrl("你的API基础地址") .addConverterFactory(GsonConverterFactory.create(gson)) // 传入自定义的Gson实例 .build() // 定义你的API接口 interface DataFeedApi { @GET("items") suspend fun getItems(): List<BaseItem> }
这样调用接口时,Retrofit就会用我们配置的Gson正确把JSON列表反序列化成TypeA/TypeB/TypeC的混合列表了!
内容的提问来源于stack exchange,提问作者user1470618
相关产品推荐
相关产品推荐

