如何在Kotlin中使用GSON解析含动态键名的JSON数据?
在Kotlin中用GSON解析动态结构的JSON
问题描述
我有如下JSON结构:
{"codemap":{"codeOfItem1":"titleOfItem1","codeOfItem2":"titleOfItem2","codeOfItem3":"titleOfItem3","codeOfItem4":"titleOfItem4"},"items":{"titleOfItem1":{"attribute1":"value1","atttribute2":{"subattr1":"value1_of_subattr1_for_item1","subattr2":"value1_of_subattr2_for_item1"}},"titleOfItem2":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item2","subattr2":"value1_of_subattr2_for_item2"}},"titleOfItem3":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item3","subattr2":"value1_of_subattr2_for_item3"}},"titleOfItem4":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item4","subattr2":"value1_of_subattr2_for_item4"}}}}
其中titleOfItemXXX既是codemap中的值,也是items中的键名。由于生产环境中条目数量和键名不确定,不想用手动解析或固定数据类,请问如何在Kotlin中使用GSON解析该JSON?
解决方案
方式1:使用Gson的JsonObject直接处理
这种方式完全适配动态结构,灵活度最高:
import com.google.gson.Gson import com.google.gson.JsonObject fun main() { val jsonString = """{"codemap":{"codeOfItem1":"titleOfItem1","codeOfItem2":"titleOfItem2","codeOfItem3":"titleOfItem3","codeOfItem4":"titleOfItem4"},"items":{"titleOfItem1":{"attribute1":"value1","atttribute2":{"subattr1":"value1_of_subattr1_for_item1","subattr2":"value1_of_subattr2_for_item1"}},"titleOfItem2":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item2","subattr2":"value1_of_subattr2_for_item2"}},"titleOfItem3":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item3","subattr2":"value1_of_subattr2_for_item3"}},"titleOfItem4":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item4","subattr2":"value1_of_subattr2_for_item4"}}}}""" val gson = Gson() val rootObject = gson.fromJson(jsonString, JsonObject::class.java) // 提取codemap并遍历 val codeMap = rootObject.getAsJsonObject("codemap") codeMap.entrySet().forEach { (code, titleJson) -> val title = titleJson.asString // 根据title从items中获取对应数据 val itemObject = rootObject.getAsJsonObject("items").getAsJsonObject(title) // 读取属性值 val attribute1 = itemObject.get("attribute1").asString val subAttr1 = itemObject.getAsJsonObject("atttribute2").get("subattr1").asString println("Code: $code, Title: $title, Attribute1: $attribute1, SubAttr1: $subAttr1") } }
方式2:用嵌套Map映射动态结构
贴近Kotlin集合操作习惯,无需额外定义类:
import com.google.gson.Gson import com.google.gson.reflect.TypeToken fun main() { val jsonString = """{"codemap":{"codeOfItem1":"titleOfItem1","codeOfItem2":"titleOfItem2","codeOfItem3":"titleOfItem3","codeOfItem4":"titleOfItem4"},"items":{"titleOfItem1":{"attribute1":"value1","atttribute2":{"subattr1":"value1_of_subattr1_for_item1","subattr2":"value1_of_subattr2_for_item1"}},"titleOfItem2":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item2","subattr2":"value1_of_subattr2_for_item2"}},"titleOfItem3":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item3","subattr2":"value1_of_subattr2_for_item3"}},"titleOfItem4":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item4","subattr2":"value1_of_subattr2_for_item4"}}}}""" val gson = Gson() // 定义嵌套Map的类型 val rootType = object : TypeToken<Map<String, Map<String, Any>>>() {}.type val rootMap: Map<String, Map<String, Any>> = gson.fromJson(jsonString, rootType) val codeMap = rootMap["codemap"] as Map<String, String> val itemsMap = rootMap["items"] as Map<String, Map<String, Any>> // 关联codemap和items数据 codeMap.forEach { (code, title) -> val item = itemsMap[title] ?: return@forEach val attribute1 = item["attribute1"] as String val subAttrMap = item["atttribute2"] as Map<String, String> val subAttr1 = subAttrMap["subattr1"] println("Code: $code, Title: $title, Attribute1: $attribute1, SubAttr1: $subAttr1") } }
方式3:半固定数据类+Map(兼顾类型安全与动态性)
如果Item内部属性固定,仅外部键名动态,可采用这种方式:
import com.google.gson.Gson import com.google.gson.annotations.SerializedName // 定义Item的固定结构(注意匹配原JSON的拼写) data class Item( val attribute1: String, @SerializedName("atttribute2") val attribute2: SubAttribute ) data class SubAttribute( val subattr1: String, val subattr2: String ) // 外层用Map接收动态键 data class RootData( val codemap: Map<String, String>, val items: Map<String, Item> ) fun main() { val jsonString = """{"codemap":{"codeOfItem1":"titleOfItem1","codeOfItem2":"titleOfItem2","codeOfItem3":"titleOfItem3","codeOfItem4":"titleOfItem4"},"items":{"titleOfItem1":{"attribute1":"value1","atttribute2":{"subattr1":"value1_of_subattr1_for_item1","subattr2":"value1_of_subattr2_for_item1"}},"titleOfItem2":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item2","subattr2":"value1_of_subattr2_for_item2"}},"titleOfItem3":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item3","subattr2":"value1_of_subattr2_for_item3"}},"titleOfItem4":{"attribute1":"value2","atttribute2":{"subattr1":"value1_of_subattr1_for_item4","subattr2":"value1_of_subattr2_for_item4"}}}}""" val gson = Gson() val rootData = gson.fromJson(jsonString, RootData::class.java) // 遍历关联数据 rootData.codemap.forEach { (code, title) -> val item = rootData.items[title] ?: return@forEach println("Code: $code, Title: $title, Attribute1: ${item.attribute1}, SubAttr1: ${item.attribute2.subattr1}") } }
内容的提问来源于stack exchange,提问作者Tauri
相关产品推荐
相关产品推荐

