Kotlinx Serialization密封类的灵活(反)序列化实现问题
Kotlin多态序列化实现灵活结构化参数配置
问题背景
通过密封类Parameters及其子类构建结构化参数体系,要求实现与常规JSON结构一致的灵活序列化/反序列化,避免使用Any导致的类型无控问题。此前尝试多态序列化时遇到报错:
kotlin.IllegalStateException: Primitives cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead
即使启用数组多态性仍无法解决。
解决方案
核心思路是自定义Parameters的序列化器,跳过默认多态的type标记,直接将容器类的value与JSON值做映射,同时在反序列化时根据JSON元素类型自动匹配对应容器类。
1. 定义密封类与子类
给每个容器类添加@SerialName(用于序列化器内部标识,不输出到JSON),并指定自定义序列化器:
@Serializable(with = ParametersSerializer::class) sealed class Parameters { @Serializable @SerialName("string") data class StringContainer(val value: String) : Parameters() @Serializable @SerialName("int") data class IntContainer(val value: Int) : Parameters() @Serializable @SerialName("map") data class MapContainer(val value: Map<String, Parameters>) : Parameters() // 扩展其他基础类型容器 @Serializable @SerialName("boolean") data class BooleanContainer(val value: Boolean) : Parameters() @Serializable @SerialName("list") data class ListContainer(val value: List<Parameters>) : Parameters() }
2. 自定义序列化器
实现KSerializer,直接处理每种Parameters子类的序列化/反序列化逻辑:
import kotlinx.serialization.* import kotlinx.serialization.descriptors.buildClassSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.* object ParametersSerializer : KSerializer<Parameters> { override val descriptor = buildClassSerialDescriptor("Parameters") override fun serialize(encoder: Encoder, value: Parameters) { when (value) { is Parameters.StringContainer -> encoder.encodeString(value.value) is Parameters.IntContainer -> encoder.encodeInt(value.value) is Parameters.BooleanContainer -> encoder.encodeBoolean(value.value) is Parameters.MapContainer -> encoder.encodeSerializableValue( MapSerializer(String.serializer(), ParametersSerializer), value.value ) is Parameters.ListContainer -> encoder.encodeSerializableValue( ListSerializer(ParametersSerializer), value.value ) // 新增类型时在此添加对应序列化逻辑 } } override fun deserialize(decoder: Decoder): Parameters { require(decoder is JsonDecoder) { "仅支持JSON格式解码" } val element = decoder.decodeJsonElement() return when (element) { is JsonPrimitive -> when { element.isString -> Parameters.StringContainer(element.content) element.intOrNull != null -> Parameters.IntContainer(element.int) element.booleanOrNull != null -> Parameters.BooleanContainer(element.boolean) else -> throw SerializationException("不支持的基本类型: $element") } is JsonObject -> Parameters.MapContainer( decoder.json.decodeFromJsonElement( MapSerializer(String.serializer(), ParametersSerializer), element ) ) is JsonArray -> Parameters.ListContainer( decoder.json.decodeFromJsonElement( ListSerializer(ParametersSerializer), element ) ) else -> throw SerializationException("不支持的JSON元素类型: $element") } } }
3. 配置容器类
保持原PluginConfiguration结构不变即可:
@Serializable data class PluginConfiguration( // 其他业务字段 val parameters: Parameters.MapContainer, )
4. 测试示例
以下代码可验证序列化/反序列化效果:
import kotlinx.serialization.json.Json fun main() { val testJson = """ { "parameters": { "key1": "String value", "key2": 12, "key3": {}, "key4": true, "key5": ["a", 3, false] } } """.trimIndent() // 反序列化 val config = Json.decodeFromString<PluginConfiguration>(testJson) println("反序列化结果: $config") // 序列化 val serializedJson = Json.encodeToString(config) println("序列化结果: $serializedJson") }
关键说明
- 自定义序列化器绕过了Kotlinx Serialization默认的多态
type标记逻辑,直接将容器类的value映射为JSON原生值,解决了基本类型无法携带多态标记的报错问题。 - 新增类型时,只需添加对应容器类,并在序列化器的
serialize和deserialize方法中补充逻辑即可。
内容的提问来源于stack exchange,提问作者Rohde Fischer
相关产品推荐
相关产品推荐

