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

Kotlin中使用Gson序列化含动态Key的JSON至对象的方案

问题:Gson序列化含动态Key的JSON响应

需求背景

调用API接口后,返回的响应对象包含固定字段和一个含动态Key的嵌套对象,格式如下:

string : {string: string}

示例JSON响应

{
    "contentName": "mobile",
    "language": "nz-en",    
    "content": {
        "data_pack_description_0": {"text": "Data only"},
        "data_pack25_description_0": {"text": "Pack 25"},
        "buying_title_0": {"text": "Buy an item"},
        "buying_description_0": {"text": "This is an item for sale"}
    }
}

当前代码问题

计划用Gson序列化到数据模型类,但content内的Key是动态的,无法提前确定,导致@SerializedName无法适配:

data class ResponseObject(
    @SerializedName("contentName")
    val contentName: String,
    @SerializedName("language")
    val language: String,
    @SerializedName("content")
    val content: Content // 此处Content类无法用固定@SerializedName定义
)

data class Content(
    // 无法提前确定Key,@SerializedName无效
)

核心问题:已知content内格式为"string": {"string" : "string"}但具体Key未知,如何用Gson序列化出适合MVVM架构ViewModel层使用的模型?


解决方案

针对动态Key的场景,Gson提供了两种简洁的处理方式,完全适配MVVM架构的使用需求:

方法1:用Map<String, TextEntry>替代固定Content类

因为content内的每个动态Key对应的值结构固定(都是{"text": "字符串"}),可以先定义一个对应值结构的data class,再用Map来承载动态Key:

// 对应content内每个动态Key的值结构
data class TextEntry(
    @SerializedName("text")
    val text: String
)

// 顶层响应模型
data class ResponseObject(
    @SerializedName("contentName")
    val contentName: String,
    @SerializedName("language")
    val language: String,
    @SerializedName("content")
    val content: Map<String, TextEntry> // 用Map承载动态Key
)

使用方式

在ViewModel层可以直接通过Key取值,比如:

// 假设已解析得到responseObject实例
val dataPackDesc = responseObject.content["data_pack_description_0"]?.text

这种方式最简单,无需额外配置,完全满足MVVM中数据传递和使用的需求。

方法2:自定义TypeAdapter(复杂场景可选)

如果需要对动态Key做额外处理(比如统一转换Key格式、过滤无效Key),可以自定义Gson的TypeAdapter:

  1. 定义Content类(用Map存储内部数据):
data class Content(
    val entries: Map<String, TextEntry>
)
  1. 自定义TypeAdapter:
class ContentTypeAdapter : TypeAdapter<Content>() {
    override fun write(out: JsonWriter, value: Content) {
        // 序列化逻辑(如需将Content转成JSON时使用)
        out.beginObject()
        value.entries.forEach { (key, entry) ->
            out.name(key)
            Gson().toJson(entry, TextEntry::class.java, out)
        }
        out.endObject()
    }

    override fun read(`in`: JsonReader): Content {
        val entries = mutableMapOf<String, TextEntry>()
        `in`.beginObject()
        while (`in`.hasNext()) {
            val key = `in`.nextName()
            val entry = Gson().fromJson(`in`, TextEntry::class.java)
            entries[key] = entry
            // 可在此添加自定义逻辑,比如过滤不符合规则的Key
        }
        `in`.endObject()
        return Content(entries)
    }
}
  1. 注册TypeAdapter到Gson实例:
val gson = GsonBuilder()
    .registerTypeAdapter(Content::class.java, ContentTypeAdapter())
    .create()
  1. 修改ResponseObject:
data class ResponseObject(
    @SerializedName("contentName")
    val contentName: String,
    @SerializedName("language")
    val language: String,
    @SerializedName("content")
    val content: Content
)

适用场景

当需要对动态Key做自定义处理时使用,比如统一去掉Key中的后缀、过滤无效条目等,常规场景下方法1足够使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 06:15:21