如何解析含键值对的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
相关产品推荐
相关产品推荐

