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

kotlinx deserialization反序列化多类型标量数组类型强转异常排查

问题场景

我尝试反序列化如下结构的JSON(实际业务场景结构更复杂,以下为核心结构示例):

[
    {
        "field": "field1",
        "value": [1000, 2000]
    },
    {
        "field": "field2",
        "value": 1
    },
    {
        "field": "field2",
        "value":["strval2","strval3"]
    },
    {
        "field": "field4",
        "value": "strval1"
    }
]

尝试多种JsonContentPolymorphicSerializer实现方案后,运行时始终抛出异常:
class java.util.ArrayList cannot be cast to class myorg.ConditionValue (java.util.ArrayList is in module java.base of loader 'bootstrap'; myorg.ConditionValue is in unnamed module of loader 'app')

原有错误实现代码如下:

@Serializable
sealed class ConditionValue

@Serializable(with = StringValueSerializer::class)
data class StringValue(val value: String) : ConditionValue()

@Serializable(with = StringListValueSerializer::class)
data class StringListValue(val value: List<StringValue>) : ConditionValue()

object ConditionSerializer : JsonContentPolymorphicSerializer<Any>(Any::class) {
    override fun selectDeserializer(element: JsonElement) = when (element) {
        is JsonPrimitive -> StringValueSerializer
        is JsonArray -> ListSerializer(StringValueSerializer)
        else -> StringValueSerializer
    }
}

object StringValueSerializer : KSerializer<StringValue> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("StringValue")

    override fun deserialize(decoder: Decoder): StringValue {
        require(decoder is JsonDecoder)
        val element = decoder.decodeJsonElement()
        return StringValue(element.jsonPrimitive.content)
    }

    override fun serialize(encoder: Encoder, value: StringValue) {
        encoder.encodeString(value.value)
    }
}
现有实现核心疏漏
  • 多态序列化器泛型指定错误:ConditionSerializer 继承时指定的基类是Any,但实际反序列化的目标基类是ConditionValue,泛型不匹配会导致类型调度逻辑和目标类型脱节,是类型转换异常的核心原因。
  • 数组分支返回的序列化器类型错误:检测到JsonArray时直接返回ListSerializer(StringValueSerializer),该序列化器反序列化的输出是JDK原生ArrayList实例,不是自定义的ConditionValue子类,无法向上转型为ConditionValue,直接触发ClassCastException。
  • 数值类型兼容缺失:示例JSON中存在数字单值、数字数组场景,现有逻辑仅处理字符串和字符串数组,遇到数值类型会直接解析失败。
  • 序列化器绑定逻辑错误:给密封类子类单独配置了序列化器,但没有将多态序列化器绑定到ConditionValue类或外层字段上,外层反序列化时不会触发自定义的类型判断逻辑。
正确实现方案

核心原则:多态序列化器的泛型必须指定为密封基类ConditionValue,所有类型分支返回的序列化器都必须输出ConditionValue的子类实例,不能返回原生基础类型/集合类型。

import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.*

// 外层业务结构
@Serializable
data class ConditionItem(
    val field: String,
    // 给value字段指定自定义多态序列化器
    @Serializable(with = ConditionValueSerializer::class)
    val value: ConditionValue
)

// 密封基类,统一由多态序列化器调度
sealed class ConditionValue

// 单值子类,覆盖字符串、数字等原始值场景
@Serializable(with = SingleValueSerializer::class)
data class SingleValue(val content: String) : ConditionValue()

// 列表值子类,覆盖字符串数组、数字数组场景
@Serializable(with = ListValueSerializer::class)
data class ListValue(val content: List<String>) : ConditionValue()

// 多态序列化器,泛型指定为ConditionValue,而非Any
object ConditionValueSerializer : JsonContentPolymorphicSerializer<ConditionValue>(ConditionValue::class) {
    override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out ConditionValue> {
        return when (element) {
            is JsonPrimitive -> SingleValueSerializer
            is JsonArray -> ListValueSerializer
            else -> throw SerializationException("不支持的value类型:$element")
        }
    }
}

// 单值类型序列化器,处理所有原始值
object SingleValueSerializer : KSerializer<SingleValue> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("SingleValue", PrimitiveKind.STRING)

    override fun deserialize(decoder: Decoder): SingleValue {
        require(decoder is JsonDecoder)
        val element = decoder.decodeJsonElement().jsonPrimitive
        // 兼容字符串、数字两种原始值,统一转字符串存储,需要保留类型可扩展子类实现
        val content = if (element.isString) element.content else element.toString()
        return SingleValue(content)
    }

    override fun serialize(encoder: Encoder, value: SingleValue) {
        encoder.encodeString(value.content)
    }
}

// 列表类型序列化器,处理所有数组值
object ListValueSerializer : KSerializer<ListValue> {
    override val descriptor: SerialDescriptor = ListSerializer(String.serializer()).descriptor

    override fun deserialize(decoder: Decoder): ListValue {
        require(decoder is JsonDecoder)
        val array = decoder.decodeJsonElement().jsonArray
        val content = array.map {
            val primitive = it.jsonPrimitive
            if (primitive.isString) primitive.content else primitive.toString()
        }
        return ListValue(content)
    }

    override fun serialize(encoder: Encoder, value: ListValue) {
        encoder.encodeSerializableValue(ListSerializer(String.serializer()), value.content)
    }
}

调用方式:

val json = Json { ignoreUnknownKeys = true }
// 直接反序列化即可
val result = json.decodeFromString<List<ConditionItem>>(/* 输入JSON字符串 */)

如果业务需要严格区分数字、字符串类型的单值/列表,只需要在ConditionValue下新增对应子类(比如IntValue、IntListValue),在selectDeserializer中增加对原始值类型、数组首元素类型的判断,返回对应子类的序列化器即可,核心逻辑无需调整。

内容的提问来源于stack exchange,提问作者agathis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:36:38