使用Moshi解析含多态record字段的JSON遇编码异常的解决方法
解决Moshi解析多态record字段为Map<String, String?>的编码异常问题
异常com.squareup.moshi.JsonEncodingException的根源是record字段的value并非全为String类型——API返回的record可能包含数字、布尔值、嵌套JSON对象/数组等非String类型,而你声明的Map<String, String?>要求value必须是String,Moshi无法自动完成跨类型转换,因此抛出异常。
由于record对应上百种类型,无法逐个实现多态适配器,自定义JsonAdapter是最优解:通过适配器统一将record的所有类型值转换为String(或null)。
步骤1:自定义JsonAdapter
编写适配器类,处理JSON中所有可能的value类型,将其转换为String表示:
class ObjectToStringMapAdapter : JsonAdapter<Map<String, String?>>() { override fun fromJson(reader: JsonReader): Map<String, String?> { val resultMap = mutableMapOf<String, String?>() reader.beginObject() while (reader.hasNext()) { val key = reader.nextName() val value = when (reader.peek()) { JsonReader.Token.NULL -> { reader.nextNull() null } JsonReader.Token.BOOLEAN -> reader.nextBoolean().toString() JsonReader.Token.NUMBER -> reader.nextString() // 直接读取数字的字符串形式 JsonReader.Token.STRING -> reader.nextString() JsonReader.Token.BEGIN_OBJECT, JsonReader.Token.BEGIN_ARRAY -> { // 嵌套对象/数组转为JSON字符串保存 reader.readJsonValue().toString() } else -> { reader.skipValue() // 跳过未知类型,也可返回null或自定义默认值 null } } resultMap[key] = value } reader.endObject() return resultMap } override fun toJson(writer: JsonWriter, value: Map<String, String?>?) { if (value == null) { writer.nullValue() return } writer.beginObject() value.forEach { (key, v) -> writer.name(key) v?.let(writer::value) ?: writer.nullValue() } writer.endObject() } }
步骤2:在数据类中绑定适配器
在record字段上添加@JsonAdapter注解,指定使用自定义适配器:
data class ApiResponse( val records: List<RecordEntry> ) data class RecordEntry( @JsonAdapter(ObjectToStringMapAdapter::class) val record: Map<String, String?> )
步骤3:正常解析API响应
创建Moshi实例并解析即可,无需额外配置:
val moshi = Moshi.Builder().build() val adapter = moshi.adapter(ApiResponse::class.java) val apiResponse = adapter.fromJson(jsonString)
自定义调整说明
如果不需要保留嵌套对象/数组的内容,可以修改适配器逻辑:
- 遇到
BEGIN_OBJECT或BEGIN_ARRAY时,直接调用reader.skipValue()并返回null - 或根据业务需求返回固定字符串(如"[object]")
内容的提问来源于stack exchange,提问作者gdrt
相关产品推荐
相关产品推荐

