当前位置: 首页 > 知识库问答 >
问题:

Gson TypeAdapter在访问无效字段时是否支持跳过值解析?

王锐
2023-03-14

我有一个数据类:

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

我想使用这个IntSafeTypeAdapter反序列化到Foo对象。但是json字符串会意外地返回一个无效的foo。所以我定制了一个Gson类型适配器来解决这个问题。

还有一个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访问一个字段,它的值是一个长值,这是会发生溢出异常(NumberFormatExctive)当解析到Int,如何跳过这个值的解析和使用kotlin数据类默认值?

谢谢你的回答!

共有1个答案

易淳
2023-03-14

最后,我定制了一个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源代码ReflecteTypeAdapterFactory来找到答案:

// 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);
                }
            }
            ...
            
        } 
    }
    ...
}

当执行ReflecteTypeAdapterFactory. BoundField.read时,如果值不是null或基元,则该值将被设置到该字段中。

但该值为null,并且是基本值,因此不会设置该字段,而是使用默认值。

 类似资料:
  • 我正在尝试更新Android BluetoothChat示例的代码,以使用Protobuf进行更结构化的数据交换。我还需要byte[]数组字段来发送任意数据,如图像字节数组,但在尝试编译时。proto文件,我得到以下错误。 协议文件/蓝牙消息。proto:8:18:应为字段名。 下面是我的. proto文件。 stackoverflow上的其他几个帖子提到byte[]可以用作文件,下面的页面也说了

  • 问题内容: 我的问题是关于ReentrantLock的使用是否可以保证字段的可见性(与synced关键字提供的方面相同)。 例如,在以下类 A中 ,由于使用了 synced关键字 ,因此无需将 sharedData 字段声明为volatile。 对于下一个使用ReentrantLock的示例,该字段上的volatile关键字是否必要? 我知道无论如何使用volatile关键字都只会对性能造成微不足

  • 问题内容: 如果我想跨多个页面(如菜单)使用一个通用的UI,推荐的这样做方法是什么? 它包含模板代码和后端控制器(类似于LiftWeb框架中的“片段”)。 我知道有一个Play菜单模块,但我对总体上如何实现更感兴趣。 问题答案: 有两种方法可以将通用视图代码包含在Play框架中。 您可以使用标签或标签。 顾名思义,extends标签是从父视图扩展的。当您创建新的应用程序时,默认情况下,扩展标记用于

  • 我已经设置了一个域名到我的服务器,我已经通过IP访问了一段时间,但现在我试图通过域名访问它。当我从IP查看时,我的react应用程序工作正常,但当我尝试通过域时,它会显示“无效主机头”。 在我的网页中,我已经(并且我已经在网上尝试了各种变体):

  • 如果和具有相同的值,则方法返回。但当我通过执行以下操作检查它们是否相同时: 然后在上向我显示错误,表示值无法解析或不是字段。 类平铺:

  • 问题内容: Elasticsearch是否保持多值字段的顺序? 即,如果我在字段中输入了以下值: (鉴于未分析字段) 我是否可以确定列表的内容将始终按照与放置列表相同的顺序返回? 在上面的示例中,我想确保“值”中第一个位置的“一个”将始终与“ values_original”等中的“ 1”相对应。 我也可以将其保留为嵌套对象,即 但我想避免开销。 如果可以保证保留多值字段中的值顺序,那么我保留两个