本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
前提
今天博主在使用 @SerializedName
注解时偶然发现当该注解 value
与 alternate
属性设置相同时抛出如下错误:
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 注解的定义:
- value 序列化或反序列化时所需的字段名称
- alternate 反序列化时字段的替代名称
从定义上来看,alternate 是 value 的子集,该字段与 value 一同使用时可以特定标识反序列化时字段的名称。我们在此不妨猜测一下,先前的报错情况有可能是由于配置 alternate 属性所导致的目标字段名称解析值重复。当然,怎么个重复法,还是得根据具体实现分析。
代码
定位到错误抛出的方法:ReflectiveTypeAdapterFactory#getBoundFields
可以看到是由于 BoundField previous
不为空导致的异常,而 previous
又是由 result
Map 的 put 返回值所赋值的
也即先前的 #getFieldNames
方法返回了重复的字段名称
定位到该方法,至此一切水落石出:
总结
从分析过程可以看出,#getFieldNames
方法会将注解的 value 或 alternate 属性全部收集用于创建 BoundField
实例对数据进行序列化与反序列化。当属性值重复时,创建了重复的对象(这里还是有些区别的,由 value 生成的对象同时配置了序列化与反序列化选项,由 alternate 创建的对象只配置了反序列化,具体不展开将,有兴趣的同学可以自己 debug 分析一下)。因此报错中指的 multiple JSON fields 指的其实是由 @SerializedName 注解的属性或无注解情况下字段的原始名称的重复。
所以对于一般的字段不一致的场景,直接配置 @SerializedName 注解的 value 属性即可解决序列化与反序列化的字段名称。至此,问题得到解决。