Android中如何解析Retrofit返回的含动态键的JSON响应
解决方案
针对你遇到的Retrofit解析动态键JSONObject为列表的问题,这里提供两种可行的解决方法:
方法一:手动解析JSONObject(快速实现)
这种方法不需要修改Retrofit的转换器配置,直接在拿到响应后手动遍历解析:
定义统一实体类
因为每个动态对象结构一致,先写好对应的数据类:data class Space( @SerializedName("area") val area: Float, @SerializedName("available") val available: Boolean, // 按实际JSON结构添加其他字段 val name: String? = null // 可选:用来存garden/balcony这类动态键名 )修改Retrofit接口
让接口返回JsonObject(用Gson的JsonObject更方便):interface ApiService { @GET("your-api-endpoint") suspend fun getSpaces(): JsonObject }遍历解析成列表
在调用接口后,取出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的类型适配器:
定义响应类和包装类
先定义包含键名和实体的包装类,以及整体响应类:data class NamedSpace( val spaceName: String, val details: Space ) data class SpaceResponse( val code: Int, val message: String, val data: List<NamedSpace> )编写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 } }配置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()修改接口返回类型
接口直接返回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
相关产品推荐
相关产品推荐

