三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

new Gson() 和 GsonBuilder().registerTypeAdapter(...).create() 得到的都是 Gson 对象,都可以调用 toJson ()。区别不是 “有参

new Gson() 和 GsonBuilder().registerTypeAdapter(...).create() 得到的都是 Gson 对象,都可以调用 toJson ()。区别不是 “有参

new Gson()GsonBuilder().registerTypeAdapter(...).create()得到的都是 Gson 对象,都可以调用 toJson ()。 区别不是 “有参 / 无参构造 Gson”,区别是:这个 Gson 实例有没有装上自定义的类型转换器ClassCodec

//版本A:裸Gson,没有注册Class类型适配器 Gson gson1 = new Gson(); //版本B:GsonBuilder构建,注册了Class类型的自定义编解码器 Gson gson2 = new GsonBuilder() .registerTypeAdapter(Class.class, new Serializer.ClassCodec()) .create();

1. 普通对象,两者完全一样

如果你的对象里没有Class<?>类型的字段,比如普通 DTO:

class User{ Long id; String name; }

gson1.toJson(user)gson2.toJson(user)输出的 JSON 字符串一模一样。 这个时候你完全可以用简单的new Gson()

2.RPC 调用消息对象(Invocation)就不一样了!

RPC 的调用报文对象,一定会携带Class类型成员:

class RpcInvocation{ private String methodName; private Class<?> interfaceClass; //❗这里是Class类型! private Class<?>[] paramTypes; //❗Class数组 private Object[] args; }

👉原生 Gson(new Gson ())处理Class字段直接翻车Gson 原生不知道怎么序列化UserApi.class这种Class对象。 直接new Gson().toJson(invocation),会把 Class 对象内部一堆 JVM 元属性全部序列化出来,生成一大坨垃圾 JSON,反序列化根本还原不回来

所以手写了ClassCodec适配器:

  • 序列化时:把Class<?>对象,转为它的全类名字符串"com.xxx.UserApi"
  • 反序列化时:拿到字符串,Class.forName(全类名),重新加载出 Class 对象

registerTypeAdapter(Class.class, new ClassCodec())就是告诉这个 Gson 实例:只要遇到 Class 类型,不要用原生逻辑,用我写的这套编解码

⚠️重点:这个适配器只属于 gson2 这个实例new Gson()出来的 gson1 完全不知道这个自定义逻辑,处理 Class 字段直接出错。

3. 不是 toJson 需要 “特殊入参”,是 Gson 实例需要提前装配适配器

toJson(object)方法本身没有变,传入的对象也没变。是 Gson 这个工具实例,内部预装了处理 Class 类型的规则。

流程:

  1. 用 GsonBuilder,注册Class类型的自定义 Codec
  2. .create()生成一个带该规则的 Gson 对象
  3. 调用gson.toJson(对象),当遍历对象字段,碰到字段类型是Class,自动执行我们写好的 ClassCodec 序列化逻辑,输出全类名字符串。

4. 反序列化那边也要同一个 Gson!

//反序列化也必须用注册过Codec的gson对象 return gson.fromJson(json,clazz);

接收方 fromJson 的时候,JSON 里面存的是全类名字符串,Gson 看到目标字段是 Class 类型,就会走 Codec 的反序列化逻辑,调用 Class.forName 还原 Class 对象。

如果接收方用裸new Gson(),拿到字符串,不知道要转 Class,直接得到字符串,类型不匹配直接报错。

一句话总结

  1. 普通 POJO:new Gson()完全够用。
  2. RPC 报文里面包含Class/Class[]字段,必须用 GsonBuilder 注册自定义适配器,生成装配好规则的 Gson 实例
  3. 不是 toJson 调用的时候传参数,是构建 Gson 工具对象的时候,提前装好特殊类型的处理逻辑

踩坑提醒

很多新手会犯:序列化用带 Codec 的 Gson,反序列化写new Gson(),两边 Gson 实例不一样,反序列化直接报错。序列化、反序列化必须用同一个注册过适配器的 Gson 实例

优化小提示

不要每次 serialize/deserialize 方法内部都 new 一遍 GsonBuilder,Gson 实例是线程安全的,可以提为静态常量,只构建一次。

//只初始化一次 private static final Gson GSON = new GsonBuilder() .registerTypeAdapter(Class.class, new Serializer.ClassCodec()) .create();
← 返回列表