You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 17:52:29