API返回对象/数组时,如何正确转换为Kotlin数据类?
解决API返回对象/数组兼容的Kotlin JSON解析问题
针对你遇到的API返回字段(supplements)有时是单个对象、有时是空数组的情况,核心思路是统一两种格式为同一种数据类型(推荐用列表),通过自定义JSON反序列化器来处理格式差异,以下是具体实现方案:
1. 定义目标数据类
首先明确supplements对应的实体类(比如Supplement),并修改ProductInfo的supplements字段为列表类型:
// 假设这是supplements对应的实体类,根据实际返回字段调整 data class Supplement( val id: String, val name: String // 其他字段根据API返回补充 ) // 修改后的ProductInfo data class ProductInfo( var product: Product?, var specifications: JsonObject?, var supplements: List<Supplement>?, // 改为List类型,统一处理对象/数组 var files: List<File>?, var feedback: Feedback? )
2. 用Gson实现自定义反序列化
如果项目使用Gson作为JSON解析库,编写自定义反序列化器来兼容两种格式:
class SupplementsDeserializer : JsonDeserializer<List<Supplement>> { override fun deserialize( json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext? ): List<Supplement>? { return when { json == null || json.isJsonNull -> null // 处理数组:空数组返回空列表,非空则解析为列表 json.isJsonArray -> { val array = json.asJsonArray if (array.isEmpty()) emptyList() else context?.deserialize(array, object : TypeToken<List<Supplement>>() {}.type) } // 处理单个对象:转为包含该对象的单元素列表 json.isJsonObject -> { context?.deserialize(json, Supplement::class.java)?.let { listOf(it) } ?: emptyList() } else -> null } } }
注册反序列化器并使用:
// 初始化带自定义解析器的Gson实例 val gson = GsonBuilder() .registerTypeAdapter(object : TypeToken<List<Supplement>>() {}.type, SupplementsDeserializer()) .create() // 解析JSON字符串 val productInfo = gson.fromJson(yourJsonString, ProductInfo::class.java)
3. 用Moshi实现自定义反序列化
如果项目使用Moshi,编写对应的JSON适配器:
class SupplementsJsonAdapter(private val itemAdapter: JsonAdapter<Supplement>) : JsonAdapter<List<Supplement>>() { override fun fromJson(reader: JsonReader): List<Supplement>? { return when (reader.peek()) { JsonReader.Token.NULL -> { reader.nextNull() null } // 处理数组:遍历解析每个元素 JsonReader.Token.BEGIN_ARRAY -> { val list = mutableListOf<Supplement>() reader.beginArray() while (reader.hasNext()) { itemAdapter.fromJson(reader)?.let { list.add(it) } } reader.endArray() list.takeIf { it.isNotEmpty() } ?: emptyList() } // 处理单个对象:转为单元素列表 JsonReader.Token.BEGIN_OBJECT -> { itemAdapter.fromJson(reader)?.let { listOf(it) } ?: emptyList() } else -> { reader.skipValue() emptyList() } } } // 序列化时可选:将列表转为单个对象(如果只有一个元素)或数组 override fun toJson(writer: JsonWriter, value: List<Supplement>?) { if (value == null) { writer.nullValue() return } if (value.size == 1) { itemAdapter.toJson(writer, value.first()) } else { writer.beginArray() value.forEach { itemAdapter.toJson(writer, it) } writer.endArray() } } }
注册适配器并使用:
// 初始化带自定义适配器的Moshi实例 val moshi = Moshi.Builder() .add(SupplementsJsonAdapter(Moshi.Builder().build().adapter(Supplement::class.java))) .build() // 解析JSON字符串 val productInfoAdapter = moshi.adapter(ProductInfo::class.java) val productInfo = productInfoAdapter.fromJson(yourJsonString)
核心逻辑说明
自定义解析器的作用是在JSON解析时自动判断字段类型:
- 若返回单个对象,将其包装为单元素列表
- 若返回数组,直接解析为列表(空数组返回空列表)
- 业务代码中只需处理
List<Supplement>?,无需再判断原数据格式
内容的提问来源于stack exchange,提问作者SoFine
相关产品推荐
相关产品推荐

