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

Kotlin多平台Android/Desktop中用kotlinx.serialization解析特殊JSON

解决kotlinx.serialization解析动态数字键JSON的问题

问题场景

你的JSON结构包含固定字段"num-of-rows",以及多个动态数字字符串键(如"2"、"3")的子对象,原数据类因items字段在JSON中不存在抛出MissingFieldException,需要将数字键子对象存入Map并处理字段缺失。

解决方案步骤

1. 定义子对象数据类(处理字段缺失)

将Item中可能缺失的字段设为可空类型或赋予默认值,避免解析子对象时抛出异常:

import kotlinx.serialization.Serializable

@Serializable
data class Item(
    val id: Int? = null, // 可选字段,缺失时默认null
    val name: String? = null // 可选字段,缺失时默认null
)

2. 定义主响应数据类

包含固定字段numOfRows(映射JSON的"num-of-rows")和items(Map类型,默认空Map防止缺失异常):

import kotlinx.serialization.Serializable

@Serializable(with = ItemsResponseSerializer::class)
data class ItemsResponse(
    val numOfRows: Int,
    val items: Map<String, Item> = emptyMap()
)

3. 实现自定义序列化器

通过自定义序列化器,将JSON中除"num-of-rows"外的所有动态键值对映射到items Map:

import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.jsonObject

object ItemsResponseSerializer : KSerializer<ItemsResponse> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ItemsResponse")

    override fun serialize(encoder: Encoder, value: ItemsResponse) {
        // 若需序列化可自行实现,此处仅处理解析逻辑
        TODO("序列化逻辑按需实现")
    }

    override fun deserialize(decoder: Decoder): ItemsResponse {
        val jsonObject = (decoder as JsonDecoder).decodeJsonElement().jsonObject
        // 处理"num-of-rows"字段缺失的情况,默认值可根据业务调整
        val numOfRows = jsonObject["num-of-rows"]?.intOrNull ?: 0
        // 过滤固定字段,将剩余键值对解析为Item并存入Map
        val items = jsonObject.filterKeys { it != "num-of-rows" }
            .mapValues { decoder.json.decodeFromJsonElement<Item>(it.value) }
        return ItemsResponse(numOfRows, items)
    }
}

4. 解析JSON

配置Json实例并解析,开启ignoreUnknownKeys可避免未知键引发异常:

import kotlinx.serialization.json.Json

val json = Json {
    ignoreUnknownKeys = true
    coerceInputValues = true // 可选,自动将无效值转为默认值
}

// 示例JSON字符串
val jsonString = """
{
  "num-of-rows": 5,
  "2": { "id": 2, "name": "Item 2" },
  "3": { "id": 3 }
}
"""

val response = json.decodeFromString<ItemsResponse>(jsonString)
println(response.numOfRows) // 输出5
println(response.items["2"]?.name) // 输出Item 2
println(response.items["3"]?.name) // 输出null

替代方案(手动解析JsonObject)

如果不想写自定义序列化器,也可以手动解析JsonObject:

import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.intOrNull

fun parseResponse(jsonString: String): ItemsResponse {
    val jsonObject = json.decodeFromString<JsonObject>(jsonString)
    val numOfRows = jsonObject["num-of-rows"]?.intOrNull ?: 0
    val items = jsonObject.filterKeys { it != "num-of-rows" }
        .mapValues { json.decodeFromJsonElement<Item>(it.value) }
    return ItemsResponse(numOfRows, items)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:53:20