使用kotlinx.serialization反序列化Kotlin枚举时忽略未知值的方案问询
解决方案
1. 通用泛型序列化器(解决重复代码问题)
先定义通用的抽象序列化器基类,所有需要忽略未知值的枚举都可以复用这套逻辑,无需重复编写核心实现:
import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.builtins.ListSerializer // 单个枚举的安全序列化器基类 abstract class SafeEnumSerializer<T : Enum<T>>( private val enumClass: Class<T>, serialName: String ) : KSerializer<T?> { override val descriptor = PrimitiveSerialDescriptor(serialName, PrimitiveKind.STRING) override fun serialize(encoder: Encoder, value: T?) { value?.let { encoder.encodeString(it.name) } } override fun deserialize(decoder: Decoder): T? { val rawValue = decoder.decodeString() return try { java.lang.Enum.valueOf(enumClass, rawValue) } catch (_: IllegalArgumentException) { null } } } // 枚举列表的安全序列化器基类 abstract class SafeEnumListSerializer<T : Enum<T>>( singleSerializer: KSerializer<T?> ) : KSerializer<List<T>> { private val delegate = ListSerializer(singleSerializer) override val descriptor = delegate.descriptor override fun serialize(encoder: Encoder, value: List<T>) { encoder.encodeSerializableValue(delegate, value) } override fun deserialize(decoder: Decoder): List<T> { return decoder.decodeSerializableValue(delegate).filterNotNull() } }
之后新增需要支持忽略未知值的枚举时,仅需声明两个对应序列化器对象即可,无需重复写逻辑:
// 针对OperatingMode的序列化器,仅需2行代码 object OperatingModeSafeSerializer : SafeEnumSerializer<OperatingMode>(OperatingMode::class.java, "OperatingMode") object OperatingModeSafeListSerializer : SafeEnumListSerializer<OperatingMode>(OperatingModeSafeSerializer)
2. 全局自动生效(无需每个字段加注解)
直接将序列化器绑定到枚举类上,所有使用该枚举的场景默认会使用该序列化器:
@Serializable(with = OperatingModeSafeSerializer::class) enum class OperatingMode { Off, On, Auto }
此时单个枚举字段无需额外配置,遇到未知值会直接反序列化为null,等价于JSON未传该字段:
@Serializable data class DemoDto( val foo: String, val mode: OperatingMode? // 未知值自动返回null,无需加额外注解 )
如果需要枚举列表也自动生效,可以在Json配置中添加序列化模块全局绑定:
import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.contextual val json = Json { ignoreUnknownKeys = true serializersModule = SerializersModule { // 绑定枚举列表的序列化器 contextual(OperatingModeSafeListSerializer) // 其他枚举的列表序列化器统一在这里注册即可 } }
列表字段仅需加@Contextual注解即可自动应用过滤逻辑:
@Serializable data class DemoListDto( val modes: @Contextual List<OperatingMode> // 自动过滤未知枚举值 )
效果验证
完全符合要求的逻辑:
- 单字段场景:
{"foo":"bar","mode":"Banana"}反序列化后mode字段为null,等价于{"foo":"bar"} - 列表场景:
{"modes":["Off","On","Banana"]}反序列化后modes为[Off, On],自动过滤未知值
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

