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
相关产品推荐
相关产品推荐

