Android中使用Moshi与Retrofit反序列化JSON:Map转List可行吗?
问题解答
1. 你写的@FromJson方法能否实现Map转List?
可以实现,但需要将自定义适配器注册到Moshi实例中,否则Moshi无法识别如何将JSON对象(对应Map<String, Person>)转换为List<Person>。
注册方式示例:
val moshi = Moshi.Builder() .add(PersonAdapter()) .build() // 将该moshi实例传入Retrofit的ConverterFactory val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(MoshiConverterFactory.create(moshi)) .build()
注册完成后,Moshi解析PersonList中的personList字段时,会自动调用PersonAdapter的getPersonList方法,把JSON里的results对象(解析为Map<String, Person>)转换为List<Person>。
2. 除手动遍历外的最佳实现方案
方案一:优化现有适配器(保证顺序)
如果需要保证List的顺序和JSON中数字键的顺序一致(比如"0"、"1"的顺序),可以修改适配器逻辑,对Map的键排序后再取值:
class PersonAdapter { @FromJson fun getPersonList(map: Map<String, Person>): List<Person> { return map.entries .sortedBy { it.key.toInt() } // 按数字键排序 .map { it.value } } }
这种方式直接针对Person类型的转换场景,代码简洁,维护成本低。
方案二:通用Map转List适配器(多类型复用)
如果项目中有多个类似的JSON结构(对象转List),可以编写通用适配器工厂,避免重复编写适配器:
class MapToListAdapterFactory : JsonAdapter.Factory { override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? { val rawType = Types.getRawType(type) // 只处理List类型的解析请求 if (rawType != List::class.java) return null // 获取List的泛型元素类型 val elementType = Types.collectionElementType(type, List::class.java) // 创建对应泛型的Map适配器 val mapAdapter = moshi.adapter<Map<String, Any>>(Types.newParameterizedType(Map::class.java, String::class.java, elementType)) return object : JsonAdapter<List<Any>>() { override fun fromJson(reader: JsonReader): List<Any>? { val map = mapAdapter.fromJson(reader) ?: return emptyList() return map.entries .sortedBy { it.key.toIntOrNull() ?: Int.MAX_VALUE } // 数字键排序,非数字键后置 .map { it.value } } override fun toJson(writer: JsonWriter, value: List<Any>?) { // 如需序列化List为JSON对象,可在此实现反向逻辑 writer.beginArray() value?.forEach { moshi.adapter(it::class.java).toJson(writer, it) } writer.endArray() } } } }
注册时只需添加该工厂:
val moshi = Moshi.Builder() .add(MapToListAdapterFactory()) .build()
该方案一次注册即可适配所有类似场景,无需为每个类型单独编写适配器。
方案三:使用计算属性替代适配器
无需自定义适配器,直接在PersonList中声明Map字段,通过计算属性自动转换为List:
data class PersonList( @Json(name = "page") val page: Int, @Json(name = "results") private val personMap: Map<String, Person> ) { // 计算属性,自动将Map转换为有序List val personList: List<Person> get() = personMap.entries.sortedBy { it.key.toInt() }.map { it.value } }
这种方式代码紧凑,无需额外适配器,适合简单场景。将personMap设为private可避免外部直接访问。
内容的提问来源于stack exchange,提问作者Raji A C
相关产品推荐
相关产品推荐

