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

如何在Kotlin中解析带索引键的JSON字典?

Kotlin解析带数字索引键的JSON伪数组问题

问题背景

作为Kotlin新手,我在解析JSON时遇到了一个小难题:需要把JSON里带有"0"、"1"这类数字索引键的picks部分解析成ArrayList<Picks>,但不知道该怎么循环处理这种结构。先贴出我的JSON示例和最初的解析代码:

示例JSON

{
  "orders": {
    "id": 1,
    "base_order": {
      "id": 1,
      "price": 10
    },
    "picks": {
      "0": { "name": "Omelette du fromage" },
      "1": { "name": "Baguette" }
    }
  }
}

最初的解析代码

Orders类

@Parcel class Orders {
    var id = 0L
    var baseOrder = BaseOrder()
    var picks = ArrayList<Picks>()
    fun loadFromJson(json: JsonObject) {
        id = readLong(json, TAG_ID)
        json.get(TAG_BASE_ORDER)?.let {
            if (it.isJsonObject) baseOrder.loadFromJson(it.asJsonObject)
        }
    }
    companion object {
        private const val TAG_ID = "id"
        private const val TAG_BASE_ORDER = "base_order"
    }
}

BaseOrder类

@Parcel class BaseOrder {
    var id = 0L
    var price = 0f
    fun loadFromJson(json: JsonObject) {
        id = readLong(json, TAG_ID)
        price = readFloat(json, TAG_PRICE)
    }
    companion object {
        private const val TAG_ID = "id"
        private const val TAG_PRICE = "price"
    }
}

Picks类

@Parcel class Picks {
    var name = ""
    fun loadFromJson(json: JsonObject) {
        name = readString(json, TAG_NAME)
    }
    companion object {
        private const val TAG_NAME = "name"
    }
}

顺便提一句,这种用数字索引作为键、模拟数组结构的JSON叫做伪数组(或对象模拟数组),它本质是JSON对象而非标准JSON数组(标准数组用[]包裹),搜这个关键词能找到更多相关解法。


我找到的解决方法

经过摸索,我终于写出了可行的解析代码,核心是遍历JSON对象的键值对来处理picks:

@Parcel class Orders {
    var id = 0L
    var baseOrder = BaseOrder()
    var picks = ArrayList<Picks>()
    var pickItem = Picks() // 临时缓冲对象
    fun loadFromJson(json: JsonObject) {
        id = readLong(json, TAG_ID)
        json.get(TAG_BASE_ORDER)?.let {
            if (it.isJsonObject) baseOrder.loadFromJson(it.asJsonObject)
        }
        // 解析picks部分
        json.get("picks")?.let {
            if (it.isJsonObject)
                it.asJsonObject.entrySet().forEach() {
                    it.value.let {
                        if (it.isJsonObject) {
                            pickItem.loadFromJson(it.asJsonObject)// 将JSON项解析到缓冲对象
                            picks.add(pickItem) // 添加到数组
                            pickItem = Picks() // 重置缓冲对象
                        }
                    }
                }
        }
    }
    companion object {
        private const val TAG_ID = "id"
        private const val TAG_BASE_ORDER = "base_order"
    }
}

这段代码能正常工作,但用了临时缓冲对象,感觉可以优化。


优化建议

作为Kotlin新手,其实可以去掉临时缓冲对象,直接在循环里创建新的Picks实例,代码会更简洁清晰:

优化后的解析逻辑

@Parcel class Orders {
    var id = 0L
    var baseOrder = BaseOrder()
    var picks = ArrayList<Picks>()
    
    fun loadFromJson(json: JsonObject) {
        id = readLong(json, TAG_ID)
        // 解析base_order
        json.get(TAG_BASE_ORDER)?.takeIf { it.isJsonObject }?.let {
            baseOrder.loadFromJson(it.asJsonObject)
        }
        // 解析picks
        json.get(TAG_PICKS)?.takeIf { it.isJsonObject }?.let { picksObj ->
            picksObj.asJsonObject.entrySet().forEach { (_, value) ->
                value.takeIf { it.isJsonObject }?.let { pickJson ->
                    val pick = Picks().apply {
                        loadFromJson(pickJson.asJsonObject)
                    }
                    picks.add(pick)
                }
            }
        }
    }
    
    companion object {
        private const val TAG_ID = "id"
        private const val TAG_BASE_ORDER = "base_order"
        private const val TAG_PICKS = "picks" // 把picks的key也定义为常量,避免硬编码
    }
}

优化点说明

  1. 去掉临时缓冲对象:直接在循环中创建Picks实例,用apply函数一次性完成初始化和解析,代码更流畅,也避免了重复重置缓冲的步骤。
  2. 用takeIf替代嵌套判断:让条件判断更简洁,可读性更强。
  3. 添加TAG_PICKS常量:把硬编码的字符串提取为常量,符合代码规范,也方便后续维护。

如果之后想进一步简化解析流程,还可以尝试用Gson、Moshi这类成熟的JSON解析库,它们支持自定义适配器来自动处理这种伪数组结构,不用手动写遍历逻辑~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:37:57