Android Kotlin基于Retrofit2实现未知动态键的JSON对象解析
动态键JSON的Kotlin + Retrofit解析方案
1. 定义固定结构的内部实体
所有动态顶层键对应的value结构完全一致,不需要为每个未知键单独定义类,直接抽离value对应的字段为独立data class即可。
对于服务端返回的下划线命名字段,直接用序列化注解做精准映射,避免依赖全局命名转换规则,兼容性更强:
// Gson 版本实体 data class VideoRight( val granted: Long, val expires: Long, val type: String, @SerializedName("product_nid") val productNid: Long ) // 如果项目用的是Moshi转换器,替换注解即可: // data class VideoRight( // val granted: Long, // val expires: Long, // val type: String, // @Json(name = "product_nid") val productNid: Long // )
- 时间戳字段统一用Long类型存储,避免整型精度溢出
- 用字段注解的方式做映射,就算全局关闭驼峰转下划线规则也不会出现字段解析为null的问题
2. 用带泛型的Map承接动态顶层结构
这类“键名动态、value结构固定”的JSON对象,不需要自定义解析适配器——不管是Gson还是Moshi转换器,都原生支持直接将这类JSON对象反序列化为Map:
- Map的key 对应JSON里的任意顶层字符串键(比如示例中的"22397"、"22313")
- Map的value 就是前面定义的
VideoRight实体
3. 编写Retrofit接口
接口方法直接返回对应的Map泛型类型,框架会自动完成全流程解析:
interface VideoApiService { @GET("your/api/right/list/path") suspend fun getVideoRights(): Map<String, VideoRight> }
注意:必须给Map写全泛型参数,不能使用无泛型的原始Map。如果省略泛型,序列化库无法识别value的目标类型,会返回原生嵌套的LinkedTreeMap结构,后续强转实体时会直接抛出类型转换异常。
4. 业务层常用适配处理
默认解析得到的Map中,每个value是不携带自己对应的顶层键的。如果业务需要把那个动态的数字键(比如"22397")作为条目的一部分使用,只需要遍历Map手动做一次转换,把key拼进实体即可:
// 定义带动态id的业务实体 data class VideoRightWithId( val itemId: String, // 对应原JSON的顶层动态键 val granted: Long, val expires: Long, val type: String, val productNid: Long ) // 把原始解析的Map转换为可直接使用的业务列表 fun convertToBizList(rawMap: Map<String, VideoRight>): List<VideoRightWithId> { return rawMap.map { (itemId, rightInfo) -> VideoRightWithId( itemId = itemId, granted = rightInfo.granted, expires = rightInfo.expires, type = rightInfo.type, productNid = rightInfo.productNid ) } }
转换后的列表可以直接配合RecyclerView、业务逻辑使用,不需要额外维护键值对应关系。
内容的提问来源于stack exchange,提问作者Jitender Sharma
相关产品推荐
相关产品推荐

