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

Kotlin使用Ktor反序列化嵌套JSON字符串遇错求助

问题描述

服务返回的JSON结构如下:

{"id":"13211","json":"{\"id\": \"5489491\", \"title\": \"Bibliothèque de la Grognarde\", \"items\": [{\"label\": \"Label\", \"type\": \"TYPE_1\", \"data\": {\"value\": \"VALUE\"}}, {\"label\": \"Label\", \"type\": \"TYPE_2\", \"data\": {\"label\": \"LABEL\"}}]}"}

我希望将其中的json字段反序列化为Response类型的数据类而非字符串,于是定义了以下数据类:

@Serializable
data class WrapperResponse(val id: String, val json: Response)

@Serializable(WrapperDeserializer::class)
data class Response(
    val id: String,
    val title: String,
    val items: List<Item>,
)

@Serializable
data class Item(
    val label: String,
    val type: ItemType,
    val data: AbstractItem?
)

@Serializable
class AbstractItem()

@Serializable
data class Type1Item(val value: String): AbstractItem()

@Serializable
data class Type2Item(val label: String): AbstractItem()

以及自定义反序列化器:

object WrapperDeserializer : KSerializer<Response> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Item") {
        element<String>("id")
        element<String>("title")
    }

    override fun deserialize(decoder: Decoder): Response {
        decoder.decodeStructure(descriptor) {
            var id: String? = null
            var title: String? = null
            while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    0 -> id = decodeStringElement(descriptor, 0)
                    1 -> title = decodeStringElement(descriptor, 1)
                    CompositeDecoder.DECODE_DONE -> break
                    else -> error("Unexpected index: $index")
                }
            }
        }
    }
}

运行时抛出JsonConvertException: Illegal input异常,提示信息为Unexpected JSON token at offset X : Expected start of the object '{', but had ' ' instead,请问哪里出错了?


问题分析与修复

1. 反序列化器逻辑完全错误

你给Response加的自定义反序列化器没处理嵌套JSON字符串的核心逻辑:服务返回的json字段是字符串格式的JSON,但你的反序列化器直接把它当成普通JSON对象解析——解码器拿到的是字符串的起始引号,而非对象的{,自然会报格式错误。

正确逻辑应该是:先读出这个字符串,再把它解析成Response对象。

2. 反序列化器的描述符与实现严重缺失

  • 描述符命名为"Item",但实际用于解析Response,命名混乱;
  • 描述符只定义了id和title,但Response还有items字段完全没处理,就算解析成功也会丢失数据;
  • deserialize方法最后没有返回Response对象,这本身就会导致编译错误(你可能漏写了)。

3. 抽象类多态反序列化未配置

Item里的data字段是AbstractItem类型,你没配置多态反序列化规则,Kotlin Serializer无法根据type字段区分Type1Item和Type2Item,后续解析items时也会报错。


修复后的完整代码

1. 配置抽象类的多态反序列化

给AbstractItem添加多态标识,指定用type字段区分子类:

@Serializable
@Polymorphic
@JsonClassDiscriminator("type")
abstract class AbstractItem()

@Serializable
@SerialName("TYPE_1")
data class Type1Item(val value: String): AbstractItem()

@Serializable
@SerialName("TYPE_2")
data class Type2Item(val label: String): AbstractItem()

@Serializable
enum class ItemType {
    TYPE_1, TYPE_2
}

2. 修正嵌套JSON的反序列化逻辑

不需要给Response加自定义序列化器,而是给WrapperResponse的json字段加专属反序列化器,处理字符串转JSON对象的逻辑:

@Serializable
data class WrapperResponse(
    val id: String,
    @Serializable(NestedJsonDeserializer::class)
    val json: Response
)

@Serializable
data class Response(
    val id: String,
    val title: String,
    val items: List<Item>
)

@Serializable
data class Item(
    val label: String,
    val type: ItemType,
    val data: AbstractItem?
)

// 处理嵌套JSON字符串的反序列化器
object NestedJsonDeserializer : KSerializer<Response> {
    private val delegate = Response.serializer()
    override val descriptor: SerialDescriptor = delegate.descriptor

    override fun deserialize(decoder: Decoder): Response {
        // 先读取嵌套的JSON字符串
        val jsonString = decoder.decodeString()
        // 再将字符串解析为Response对象
        return Json.decodeFromString(delegate, jsonString)
    }

    override fun serialize(encoder: Encoder, value: Response) {
        // 如果需要序列化,把Response转为JSON字符串
        val jsonString = Json.encodeToString(delegate, value)
        encoder.encodeString(jsonString)
    }
}

注意事项

  • 确保使用的Kotlin Serializer版本支持@JsonClassDiscriminator和多态解析(1.3+版本均支持);
  • 服务返回的JSON字符串中的转义字符,decodeFromString会自动处理,无需手动转义。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 16:58:15