Gson @SerializedName 注解报 declares multiple JSON fields 问题解析

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

前提

今天博主在使用 @SerializedName 注解时偶然发现当该注解 valuealternate 属性设置相同时抛出如下错误:

class com.illtamer.infinite.bot.api.event.Event declares multiple JSON fields named post_type
复制代码

直接翻译 Gson 报错可知是由于定义了重复的 json 字段导致的该问题,经查阅发现该报错多存在于继承类与父类定义了相同字段的情况。可是博主反序列化的类长这个样子呀:

public class Event {
    // {post_type: message}
    @SerializedName(value = "post_type", alternate = "post_type")
    private String postType;
}
复制代码

而后当博主将该字段的 value 或 alternate 属性修改为其它值或删去 alternate 属性后程序可以正常允许。

分析

我们先查看 @SerializedName 注解的定义:

image.png

  • value 序列化或反序列化时所需的字段名称
  • alternate 反序列化时字段的替代名称

从定义上来看,alternate 是 value 的子集,该字段与 value 一同使用时可以特定标识反序列化时字段的名称。我们在此不妨猜测一下,先前的报错情况有可能是由于配置 alternate 属性所导致的目标字段名称解析值重复。当然,怎么个重复法,还是得根据具体实现分析。

代码

定位到错误抛出的方法:ReflectiveTypeAdapterFactory#getBoundFields

image.png

可以看到是由于 BoundField previous 不为空导致的异常,而 previous 又是由 result Map 的 put 返回值所赋值的

image.png

也即先前的 #getFieldNames 方法返回了重复的字段名称

image.png

定位到该方法,至此一切水落石出:

image.png

总结

从分析过程可以看出,#getFieldNames 方法会将注解的 value 或 alternate 属性全部收集用于创建 BoundField 实例对数据进行序列化与反序列化。当属性值重复时,创建了重复的对象(这里还是有些区别的,由 value 生成的对象同时配置了序列化与反序列化选项,由 alternate 创建的对象只配置了反序列化,具体不展开将,有兴趣的同学可以自己 debug 分析一下)。因此报错中指的 multiple JSON fields 指的其实是由 @SerializedName 注解的属性或无注解情况下字段的原始名称的重复。

所以对于一般的字段不一致的场景,直接配置 @SerializedName 注解的 value 属性即可解决序列化与反序列化的字段名称。至此,问题得到解决。

猜你喜欢

转载自juejin.im/post/7120272368856989709