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

Android中如何解析Retrofit返回的含动态键的JSON响应

解决方案

针对你遇到的Retrofit解析动态键JSONObject为列表的问题,这里提供两种可行的解决方法:

方法一:手动解析JSONObject(快速实现)

这种方法不需要修改Retrofit的转换器配置,直接在拿到响应后手动遍历解析:

  1. 定义统一实体类
    因为每个动态对象结构一致,先写好对应的数据类:

    data class Space(
        @SerializedName("area") val area: Float,
        @SerializedName("available") val available: Boolean,
        // 按实际JSON结构添加其他字段
        val name: String? = null // 可选:用来存garden/balcony这类动态键名
    )
    
  2. 修改Retrofit接口
    让接口返回JsonObject(用Gson的JsonObject更方便):

    interface ApiService {
        @GET("your-api-endpoint")
        suspend fun getSpaces(): JsonObject
    }
    
  3. 遍历解析成列表
    在调用接口后,取出data对应的JsonObject,遍历所有键值对转成实体类:

    val apiResponse = apiService.getSpaces()
    val dataObject = apiResponse.getAsJsonObject("data")
    val spaceList = mutableListOf<Space>()
    val gson = Gson()
    
    // 遍历所有动态键值对
    for ((key, jsonElement) in dataObject.entrySet()) {
        if (jsonElement.isJsonObject) {
            val space = gson.fromJson(jsonElement, Space::class.java)
            // 把动态键名存入实体类
            spaceList.add(space.copy(name = key))
        }
    }
    
    // spaceList就是最终要展示的列表
    

方法二:自定义Gson TypeAdapter(优雅适配Retrofit)

如果想让Retrofit直接把data解析成列表,不用手动处理,可以自定义Gson的类型适配器:

  1. 定义响应类和包装类
    先定义包含键名和实体的包装类,以及整体响应类:

    data class NamedSpace(
        val spaceName: String,
        val details: Space
    )
    
    data class SpaceResponse(
        val code: Int,
        val message: String,
        val data: List<NamedSpace>
    )
    
  2. 编写TypeAdapter
    实现一个TypeAdapter,将JsonObject转成List<NamedSpace>:

    class NamedSpaceListAdapter : TypeAdapter<List<NamedSpace>>() {
        override fun write(out: JsonWriter, value: List<NamedSpace>?) {
            // 序列化不需要的话直接抛异常
            throw UnsupportedOperationException("Serialization not supported")
        }
    
        override fun read(`in`: JsonReader): List<NamedSpace> {
            val list = mutableListOf<NamedSpace>()
            val gson = Gson()
            
            `in`.beginObject()
            while (`in`.hasNext()) {
                val spaceName = `in`.nextName()
                val spaceDetails = gson.fromJson(`in`, Space::class.java)
                list.add(NamedSpace(spaceName, spaceDetails))
            }
            `in`.endObject()
            
            return list
        }
    }
    
  3. 配置Retrofit的Gson转换器
    在创建Retrofit实例时,注册自定义的TypeAdapter:

    val customGson = GsonBuilder()
        .registerTypeAdapter(
            object : TypeToken<List<NamedSpace>>() {}.type,
            NamedSpaceListAdapter()
        )
        .create()
    
    val retrofit = Retrofit.Builder()
        .baseUrl("your-base-url")
        .addConverterFactory(GsonConverterFactory.create(customGson))
        .build()
    
  4. 修改接口返回类型
    接口直接返回SpaceResponse,调用时就能直接拿到解析好的列表:

    interface ApiService {
        @GET("your-api-endpoint")
        suspend fun getSpaces(): SpaceResponse
    }
    
    // 调用示例
    val response = apiService.getSpaces()
    val spaceList = response.data // 直接得到List<NamedSpace>
    

注意事项

  • 确保Space类的字段与JSON中每个动态对象的字段完全匹配,名称不一致时用@SerializedName注解映射。
  • 手动解析时建议用try-catch包裹JSON解析代码,避免出现解析异常导致崩溃。
  • 如果之前用固定字段的data class接收data为空,是因为Gson无法将动态键的JSONObject映射到固定字段的类上,所以必须用上述动态解析的方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 19:32:09