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

