You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 23:06:05