首页 > 解决方案 > Gson TypeAdapter 在访问无效字段时是否支持跳过值解析?

问题描述

背景

我有一个数据类:

data class Foo(
    @SerializedName("foo")
    val foo: Int = 1000
)

我想使用这个 IntSafeTypeAdapter 反序列化为 Foo 对象。但是 json 字符串会foo意外返回一个无效的。所以我定制了一个 Gson TypeAdapter 来解决它。

还有一个 Gson 类型适配器:

class IntSafeTypeAdapter : TypeAdapter<Int>() {
    override fun write(reader: JsonWriter?, intValue: Int?) {
        reader?.value(intValue ?: return)
    }

    override fun read(reader: JsonReader?): Int {
        val numStr = reader?.nextString()
        if (numStr == null) {
            // 1. How to skip this field and using kotlin data class default value
            // when there is no value of this field?
            return 0
        }
        if (numStr.toLong().toInt() != numStr.toInt()) {
            // 2. How to skip this field and using kotlin data class
            // when there is a long value?
            return numStr.toInt()
        }
        // common condition
        return numStr.toInt()
    }
}

问题

我的问题是对代码的评论:

  1. 当 gson 访问其值为 null 的字段时,如何跳过该值的解析并改用 kotlin 数据类默认值?
  2. 当 gson 访问一个其值为 long 值的字段时,解析为 Int 时会发生溢出异常(NumberFormatException),如何跳过该值的解析并改用 kotlin 数据类默认值?

感谢您的回答!

标签: androidgson

解决方案


最后,我自定义了一个 TypeAdapter 来做到这一点:

class IntSafeTypeAdapter: TypeAdapter<Int?>() {
    override fun write(reader: JsonWriter?, intValue: Int?) {
        reader?.value(intValue ?: return)
    }

    override fun read(reader: JsonReader?): Int? {
        val numberStr = reader?.nextString() ?: return null
        return try {
            numberStr.toInt()
        } catch (e: NumberFormatException) {
            // if return null, primitive field will not be set value
            null
        }
    }
}

并在构建 Gson 实例时注册TypeAdapter:

val gson by lazy {
    GsonBuilder().registerTypeAdapter(Int::class.java, IntSafeTypeAdapter())
        .create()
}

它是如何工作的?我们可以阅读 Gson 源代码ReflectiveTypeAdapterFactory来找到答案:

// ReflectiveTypeAdapterFactory
public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
    ...
    private ReflectiveTypeAdapterFactory.BoundField createBoundField(...) {
        ...
        return new ReflectiveTypeAdapterFactory.BoundField(...) {
            ...
            @Override void read(JsonReader reader, Object value) 
                    throws IOException, IllegalAccessException {
                Object fieldValue = typeAdapter.read(reader);
                if (fieldValue != null || !isPrimitive) {  // <<<<<< here
                    field.set(value, fieldValue);
                }
            }
            ...
            
        } 
    }
    ...
}

执行时ReflectiveTypeAdapterFactory.BoundField.read,如果 value 不为 null 或不是原始的,则 value 将设置到该字段中。

但是该值为 null 并且它是原始的,因此不会设置该字段并使用默认值。


推荐阅读