如何让Moshi适配器忽略多态列表中格式错误的已知类型元素?
解决Moshi多态解析时忽略缺失必填字段的已知类型元素
方案思路
通过装饰器模式包装子类的JsonAdapter,捕获单个子类解析时的JsonDataException,返回null;最后在列表解析完成后过滤掉null元素,既保留必填字段的类型安全,又能跳过无效元素。
具体实现步骤
1. 实现容错Adapter装饰器
创建通用的LenientAdapter,用来包装目标Adapter并捕获解析异常:
class LenientAdapter<T>(private val delegate: JsonAdapter<T>) : JsonAdapter<T>() { override fun fromJson(reader: JsonReader): T? { return try { delegate.fromJson(reader) } catch (e: JsonDataException) { // 捕获必填字段缺失等解析异常,返回null标记无效元素 null } } override fun toJson(writer: JsonWriter, value: T?) { delegate.toJson(writer, value) } }
2. 配置Moshi时绑定容错Adapter
在Moshi构建过程中,给每个多态子类绑定包装后的容错Adapter,同时保留未知类型的默认值处理:
val moshi = Moshi.Builder() .add( PolymorphicJsonAdapterFactory.of(Animal::class.java, "type") .withSubtype(Fish::class.java, Type.FISH.name) .withSubtype(Bird::class.java, Type.BIRD.name) .withDefaultValue(null) // 未知类型直接返回null,后续统一过滤 ) // 为Fish类绑定容错Adapter .add(object : ObjectFactory<Fish> { override fun create(): Fish = throw UnsupportedOperationException() }) { type, annotations, moshi -> if (type == Fish::class.java) LenientAdapter(moshi.adapter(type, annotations)) else null } // 为Bird类绑定容错Adapter .add(object : ObjectFactory<Bird> { override fun create(): Bird = throw UnsupportedOperationException() }) { type, annotations, moshi -> if (type == Bird::class.java) LenientAdapter(moshi.adapter(type, annotations)) else null } .build()
3. 解析列表后过滤无效元素
定义包含列表的数据类,解析完成后过滤掉所有null元素:
data class AnimalList(val animals: List<Animal?>) // 解析示例 val json = """{ "animals": [ { "type": "FISH" }, { "type": "ANIMAL", "requiredField": "blablabla" }, { "type": "BIRD", "requiredField": "sparrow" } ] }""" val adapter = moshi.adapter(AnimalList::class.java) val animalList = adapter.fromJson(json) val validAnimals = animalList?.animals?.filterNotNull() ?: emptyList() // validAnimals 仅包含正常解析的Bird实例
方案优势
- 保留Kotlin数据类的必填字段定义,保证类型安全,无需将所有字段改为可选
- 统一处理未知类型和已知类型解析失败的场景,最终得到纯净的有效元素列表
- 装饰器模式对原有代码侵入性低,可快速扩展到其他多态子类
内容的提问来源于stack exchange,提问作者Camilo Sierra Rodriguez
相关产品推荐
相关产品推荐

