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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 16:24:32