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

