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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 17:05:04