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

如何用Gson解析含数字键的JSON到Kotlin对象?

解决Gson解析动态数字键的JSON问题

我明白你遇到的困扰了——当JSON里出现动态变化的数字键时,常规的@SerializedName确实派不上用场,因为这个注解只能映射固定的键名,而你的Id数组里每个对象的键都是不确定的数字(1、2...)。下面我给你两种实用的解决方案,用Gson就能轻松搞定:

方案一:用Map快速处理动态键

这是最简洁的方法,直接用Map<String, Student>接收每个带数字键的对象,之后再转换成你想要的结构:

1. 定义基础数据类

首先定义存储学生信息的类,注意class是Kotlin的关键字,得用@SerializedName映射到合法的字段名:

data class Student(
    val name: String,
    @SerializedName("class") // 映射JSON里的"class"键
    val className: String
)

然后定义顶层的响应类,用List<Map<String, Student>>接收Id数组:

data class StudentResponse(
    val Id: List<Map<String, Student>>
)

2. 解析并转换数据

解析后遍历Map,把动态数字键提取成你需要的字符串字段(比如studentId):

// 假设jsonString是你的JSON响应字符串
val gson = Gson()
val response = gson.fromJson(jsonString, StudentResponse::class.java)

// 转换成包含studentId的对象列表
val studentWithIdList = response.Id.flatMap { map ->
    map.entries.map { entry ->
        // 这里可以自定义你的目标对象
        StudentWithId(
            studentId = entry.key,
            name = entry.value.name,
            className = entry.value.className
        )
    }
}

// 自定义的目标数据类
data class StudentWithId(
    val studentId: String,
    val name: String,
    val className: String
)

方案二:自定义TypeAdapter(更优雅的封装)

如果你不想暴露Map的细节,希望直接解析成自定义对象,可以写一个Gson的TypeAdapter来处理动态键的解析:

1. 定义目标数据类

直接定义包含studentId的类:

data class StudentEntry(
    val studentId: String,
    val name: String,
    @SerializedName("class")
    val className: String
)

2. 编写TypeAdapter

这个适配器会读取动态数字键,并把它赋值给studentId字段:

class StudentEntryTypeAdapter(private val gson: Gson) : TypeAdapter<StudentEntry>() {
    override fun write(out: JsonWriter, value: StudentEntry?) {
        // 如果需要序列化可以实现,这里只处理解析,暂时空实现
        out.beginObject()
        value?.let {
            out.name(it.studentId)
            gson.toJson(Student(it.name, it.className), Student::class.java, out)
        }
        out.endObject()
    }

    override fun read(`in`: JsonReader): StudentEntry {
        `in`.beginObject()
        // 读取动态数字键作为studentId
        val studentId = `in`.nextName()
        // 读取键对应的Student对象
        val student = gson.fromJson<Student>(`in`, Student::class.java)
        `in`.endObject()
        return StudentEntry(studentId, student.name, student.className)
    }

    // 辅助的Student类
    private data class Student(val name: String, @SerializedName("class") val className: String)
}

3. 注册适配器并解析

创建Gson实例时注册适配器,然后直接解析成目标类:

val gson = GsonBuilder()
    .registerTypeAdapter(StudentEntry::class.java, StudentEntryTypeAdapter(Gson()))
    .create()

// 顶层响应类
data class StudentResponse(
    val Id: List<StudentEntry>
)

// 解析JSON
val response = gson.fromJson(jsonString, StudentResponse::class.java)
// 现在response.Id里的每个元素都是带studentId的StudentEntry对象了

为什么@SerializedName没用?

再强调下:@SerializedName是用来将固定的JSON键名映射到Kotlin类的字段名,而你的场景中键是动态变化的(每次可能是1、2、3...不同的数字),注解无法预知这些动态键,所以必须用Map或自定义适配器来处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 12:02:33