如何在Moshi中处理字段类型不匹配数据并设目标属性为null
问题描述
给定如下JSON数据:
{ "items":[ { "id":"0000301553526", "values":{ "name":{ "value":{ "en_En":"Item" }, "formatted":{ }, "attribute":{ "group":{ "code":null, "label":null }, "group_mod":{ "code":null, "label":null }, "localizable":true, "type":"text", "code":"name", "label":"name", "visible":false, "display_order":null } }, "additonal_data":{ "value":"", "attribute":{ "group":{ "code":null, "label":null }, "group_mod":{ "code":null, "label":null }, "localizable":true, "type":"local", "code":"ABCC", "label":"action", "visible":false, "display_order":null } } } } ] }
其中additonal_data对象的value字段预期应为null或String类型,但后端偶尔会返回数组类型。需要忽略类型错误的数据,将该属性设为null。
当前已有如下代码:
class IgnoreDataAdapter<T> private constructor( private val delegate: JsonAdapter<T> ) : JsonAdapter<T>() { @FromJson override fun fromJson(reader: JsonReader): T? { reader.setFailOnUnknown(false) val peeked = reader.peekJson() val result: T? = try { delegate.fromJson(peeked) } catch (e: JsonDataException) { null } finally { peeked.close() } reader.skipValue() return result } @ToJson override fun toJson(writer: JsonWriter, value: T?) { delegate.toJson(writer, value) } }
但该适配器针对整个对象,希望无需逐个解析所有属性,仅将类型错误的目标属性设为null,该如何实现?
解决方案
无需修改整个对象的解析逻辑,只需要给目标字段(additonal_data.value)单独编写自定义适配器,在解析时捕获类型错误并返回null即可。
步骤1:编写单个属性的类型容错适配器
创建专门处理String?类型的适配器,遇到非String/非null的类型时直接返回null:
class SafeStringAdapter : JsonAdapter<String?>() { @FromJson override fun fromJson(reader: JsonReader): String? { return try { // 正常解析String或null if (reader.peek() == JsonToken.NULL) { reader.nextNull() null } else { reader.nextString() } } catch (e: JsonDataException) { // 类型不匹配时跳过当前值并返回null reader.skipValue() null } } @ToJson override fun toJson(writer: JsonWriter, value: String?) { writer.value(value) } }
步骤2:在数据类的目标字段上绑定适配器
假设对应的AdditonalData数据类如下,直接在value字段上用@JsonAdapter指定适配器:
data class AdditonalData( @JsonAdapter(SafeStringAdapter::class) val value: String?, val attribute: Attribute ) // 关联数据类示例 data class Values( val name: Name, val additonal_data: AdditonalData ) data class Item( val id: String, val values: Values ) data class ApiResponse( val items: List<Item> )
原理说明
- 该适配器仅作用于标记了
@JsonAdapter的单个字段,不会影响其他属性的正常解析。 - 当后端返回数组等非预期类型时,
reader.nextString()会抛出JsonDataException,此时捕获异常、跳过错误值并返回null。 - 正常的String或null类型会被正常解析,不会触发异常逻辑。
扩展:通用类型容错适配器(可选)
如果后续有其他字段需要类似处理,可以编写通用版本的适配器支持指定目标类型:
class SafeTypeAdapter<T>(private val typeAdapter: JsonAdapter<T>) : JsonAdapter<T?>() { @FromJson override fun fromJson(reader: JsonReader): T? { return try { typeAdapter.fromJson(reader) } catch (e: JsonDataException) { reader.skipValue() null } } @ToJson override fun toJson(writer: JsonWriter, value: T?) { typeAdapter.toJson(writer, value) } companion object { fun <T> create(typeAdapter: JsonAdapter<T>): SafeTypeAdapter<T> { return SafeTypeAdapter(typeAdapter) } } }
使用时可在字段上直接指定:
@JsonAdapter(SafeTypeAdapter::class) val value: String?
不过对于String这类简单类型,使用专用适配器更简洁。
内容的提问来源于stack exchange,提问作者Unii
相关产品推荐
相关产品推荐

