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

如何用kotlinx.serialization将Kotlin密封类序列化为自定义JSON结构

解决kotlinx.serialization密封接口Filter的动态字段序列化问题

问题核心

需要将Filter.NOT(Filter.Field("foo", "bar"))序列化为:

{   
    "not": {
        "foo": "bar"
    }
}

核心需求是Field类输出动态键值对结构,而非带类型标识或固定字段的JSON。

问题原因分析

  1. 自定义序列化器实现错误:原Field的companion object序列化器中,encodeStructure仅创建了Map但未调用编码方法,导致序列化器错误识别为HashMap类型,输出带类型标识的JSON。
  2. 密封接口默认类型判别:移除自定义序列化器后,Field作为密封接口子类会被自动添加类型字段(如"type": "Field"),不符合需求。
  3. JsonTransformingSerializer空指针:companion object初始化顺序问题,导致调用serializer()时实例未加载。

修复方案

1. 重新实现Field的自定义序列化器

将序列化器改为独立object(避免初始化顺序问题),正确输出动态键值对:

import kotlinx.serialization.*
import kotlinx.serialization.descriptors.mapSerialDescriptor
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.Json

@Serializable
sealed interface Filter {
    @Serializable
    @SerialName("not")
    class NOT(val not: Filter) : Filter

    // 其他逻辑类(AND/OR)保持原有正确实现
    @Serializable
    @SerialName("and")
    class AND(val and: List<Filter>) : Filter

    @Serializable(with = FieldSerializer::class)
    data class Field(val name: String, val value: String) : Filter
}

// 独立的Field序列化器,避免companion object初始化问题
object FieldSerializer : KSerializer<Filter.Field> {
    override val descriptor: SerialDescriptor = mapSerialDescriptor<String, String>()

    override fun serialize(encoder: Encoder, value: Filter.Field) {
        // 直接输出单键值对的Map结构
        encoder.encodeStringMap(mapOf(value.name to value.value))
    }

    override fun deserialize(decoder: Decoder): Filter.Field {
        val map = decoder.decodeStringMap()
        if (map.size != 1) throw SerializationException("Field必须包含且仅包含一个键值对")
        val (name, value) = map.entries.single()
        return Filter.Field(name, value)
    }
}

2. 测试序列化

使用默认或自定义Json配置测试:

fun main() {
    val json = Json { prettyPrint = true }
    val filter = Filter.NOT(Filter.Field("foo", "bar"))
    println(json.encodeToString(filter))
}

输出结果

{
    "not": {
        "foo": "bar"
    }
}

关键修复点说明

  • 独立序列化器:避免companion object初始化顺序导致的空指针异常,确保序列化器实例在使用前完全加载。
  • 正确的序列化逻辑:使用encodeStringMap直接输出动态键值对,而非错误地在encodeStructure中返回Map。
  • 密封接口的@SerialName:NOT类的@SerialName("not")确保序列化时将"not"作为外层键,包裹内部的Filter实例,符合API要求。

内容的提问来源于stack exchange,提问作者Jan Vladimir Mostert

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 16:25:03