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

如何解析含键值对的JSONObject?Retrofit适配非标准JSON至DTO

遇到这种非标准的JSON结构确实挺闹心的,不过有几个实用的办法能搞定,我给你捋捋:

方案1:用Map接收+转换方法(最快上手)

最简单的方式就是直接用Map<String, String>来接收translations字段,然后在DTO里加一个方法把这个Map转换成有序的列表——毕竟你的键都是带数字索引的,排序后就能得到和数组一样的顺序。

代码示例:

data class TranslationsDto(
    val translations: Map<String, String>?,
    val hash: String?,
    val length: Int?,
    val language: String?
) {
    // 把Map转换成按索引排序的列表
    fun getTranslationsList(): List<String> {
        return translations?.entries
            ?.sortedBy { entry ->
                // 从键的最后一段提取数字索引,比如从"my.key.nr.0"拿到0
                entry.key.split(".").last().toIntOrNull() ?: Int.MAX_VALUE
            }
            ?.map { it.value }
            ?: emptyList()
    }
}

使用的时候直接调用dto.getTranslationsList()就能得到有序的翻译值列表了,不用额外配置Retrofit,非常省心。

方案2:自定义Gson Json适配器(更整洁的DTO)

如果你希望DTO里直接用List<String>而不是Map,可以给Gson写一个自定义的TypeAdapter,让它直接把这个键值对结构解析成列表。

首先写适配器类:

class TranslationsListAdapter : TypeAdapter<List<String>>() {
    override fun write(out: JsonWriter, value: List<String>) {
        // 如果需要序列化(把列表转成原JSON结构)可以实现这段,解析的话暂时可以保留默认或者按需写
        out.beginObject()
        value.forEachIndexed { index, s ->
            out.name("my.key.nr.$index").value(s)
        }
        out.endObject()
    }

    override fun read(`in`: JsonReader): List<String> {
        val tempList = mutableListOf<Pair<Int, String>>()
        `in`.beginObject()
        
        // 遍历所有键值对,提取索引和对应的值
        while (`in`.hasNext()) {
            val key = `in`.nextName()
            val value = `in`.nextString()
            val index = key.split(".").last().toIntOrNull()
            
            index?.let { tempList.add(it to value) }
        }
        
        `in`.endObject()
        // 按索引排序后返回值列表
        return tempList.sortedBy { it.first }.map { it.second }
    }
}

然后在DTO的字段上加上@JsonAdapter注解:

import com.google.gson.annotations.JsonAdapter

data class TranslationsDto(
    @JsonAdapter(TranslationsListAdapter::class)
    val translations: List<String>?,
    val hash: String?,
    val length: Int?,
    val language: String?
)

最后确保你的Retrofit用的是配置好这个适配器的Gson实例就行,这样解析出来的translations直接就是有序的列表,不用再做转换。

方案3:Moshi版本的自定义适配器(如果用Moshi的话)

如果你的项目用的是Moshi而不是Gson,思路和上面类似,只是适配器的写法稍有不同:

import com.squareup.moshi.FromJson
import com.squareup.moshi.ToJson
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter

class TranslationsListAdapter {
    @FromJson
    fun fromJson(reader: JsonReader): List<String> {
        val tempList = mutableListOf<Pair<Int, String>>()
        reader.beginObject()
        
        while (reader.hasNext()) {
            val key = reader.nextName()
            val value = reader.nextString()
            val index = key.split(".").last().toIntOrNull()
            
            index?.let { tempList.add(it to value) }
        }
        
        reader.endObject()
        return tempList.sortedBy { it.first }.map { it.second }
    }

    @ToJson
    fun toJson(writer: JsonWriter, value: List<String>) {
        writer.beginObject()
        value.forEachIndexed { index, s ->
            writer.name("my.key.nr.$index").value(s)
        }
        writer.endObject()
    }
}

然后在DTO字段上加上@JsonAdapter,或者在Moshi实例里注册这个适配器就可以了。


内容的提问来源于stack exchange,提问作者Thomas Cirksena

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:33:12