如何为Gson中的Option自定义无冗余的序列化与反序列化器?
解决Gson中Option的序列化/反序列化问题(含null值处理)
你遇到的问题其实是Gson的默认行为导致的:当反序列化时遇到JSON null,如果目标类型是引用类型(比如Option<T>),Gson会直接返回null,而不会调用你注册的JsonDeserializer,这就是为什么你的if (json.isJsonNull())分支永远不会执行。
要解决这个问题,我们需要使用**TypeAdapter和TypeAdapterFactory**来替代JsonSerializer/JsonDeserializer,因为TypeAdapter可以完全控制序列化和反序列化的全过程,包括null值的处理。
解决方案代码实现
1. 实现Option的TypeAdapterFactory
这个工厂类负责为任意泛型参数的Option<T>创建对应的TypeAdapter:
import com.google.gson.*; import com.google.gson.reflect.TypeToken; import io.vavr.control.Option; import java.lang.reflect.Type; public class OptionTypeAdapterFactory implements TypeAdapterFactory { @SuppressWarnings("unchecked") @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { // 只处理Option类型 if (!Option.class.isAssignableFrom(typeToken.getRawType())) { return null; } // 获取Option的泛型参数类型 Type typeOfT = typeToken.getType(); if (typeOfT instanceof java.lang.reflect.ParameterizedType) { typeOfT = ((java.lang.reflect.ParameterizedType) typeOfT).getActualTypeArguments()[0]; } else { // 如果是原始的Option(无泛型),默认用Object类型 typeOfT = Object.class; } TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(typeOfT)); return (TypeAdapter<T>) new OptionTypeAdapter<>(adapter); } // 内部类:具体处理Option的序列化和反序列化 private static class OptionTypeAdapter<T> extends TypeAdapter<Option<T>> { private final TypeAdapter<T> elementAdapter; OptionTypeAdapter(TypeAdapter<T> elementAdapter) { this.elementAdapter = elementAdapter; } @Override public void write(JsonWriter out, Option<T> value) throws java.io.IOException { if (value.isEmpty()) { out.nullValue(); } else { elementAdapter.write(out, value.get()); } } @Override public Option<T> read(JsonReader in) throws java.io.IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return Option.none(); } else { return Option.some(elementAdapter.read(in)); } } } }
2. 注册Factory到GsonBuilder
创建Gson实例时,把这个工厂注册进去:
Gson gson = new GsonBuilder() .registerTypeAdapterFactory(new OptionTypeAdapterFactory()) .create();
为什么这个方案能解决问题?
TypeAdapter的read方法会被Gson调用,无论JSON是null还是非null值,所以我们可以在read方法中直接处理JsonToken.NULL的情况,返回Option.none()。- 通过
TypeAdapterFactory,我们可以自动处理任意泛型参数的Option<T>,不需要手动指定typeOfT,比你之前的OptionJsonAdapter更灵活。
测试验证
比如我们有一个包含Option的类:
class User { private String name; private Option<String> email; // 构造器、getter、setter省略 }
序列化测试:
User user1 = new User("Alice", Option.none()); String json1 = gson.toJson(user1); // 输出: {"name":"Alice","email":null} User user2 = new User("Bob", Option.some("bob@example.com")); String json2 = gson.toJson(user2); // 输出: {"name":"Bob","email":"bob@example.com"}
反序列化测试:
// 反序列化null的email String jsonNull = "{\"name\":\"Charlie\",\"email\":null}"; User userNull = gson.fromJson(jsonNull, User.class); System.out.println(userNull.getEmail().isEmpty()); // 输出: true // 反序列化非null的email String jsonSome = "{\"name\":\"Dave\",\"email\":\"dave@example.com\"}"; User userSome = gson.fromJson(jsonSome, User.class); System.out.println(userSome.getEmail().get()); // 输出: dave@example.com
这样就完全满足你的需求:无冗余序列化,且序列化反序列化后Option值保持一致。
内容的提问来源于stack exchange,提问作者luochen1990
相关产品推荐
相关产品推荐

