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:
- 定义Content类(用Map存储内部数据):
data class Content( val entries: Map<String, TextEntry> )
- 自定义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) } }
- 注册TypeAdapter到Gson实例:
val gson = GsonBuilder() .registerTypeAdapter(Content::class.java, ContentTypeAdapter()) .create()
- 修改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
相关产品推荐
相关产品推荐

