【问题标题】:How to serialize Optional<T> classes with Gson?如何使用 Gson 序列化 Optional<T> 类?
【发布时间】:2012-08-23 02:02:54
【问题描述】:

我有一个具有以下属性的对象。

private final String messageBundle;
private final List<String> messageParams;
private final String actionBundle;
private final Map<String, String> data;
private final Optional<Pair<Integer,TimeUnit>> ttl;
private final Optional<Integer> badgeNumber;
private final Optional<String> collapseKey;

该对象在一个库中,我不想仅仅为了序列化目的而对其进行修改,并且希望避免创建另一个 DTO 的成本。

如何序列化/反序列化可选属性? Optional 没有默认构造函数(既不是 apache commons Pair),但我不能使用 InstanceCreator,也不太了解如何创建一个 TypeAdapter,它将简单地将序列化委托给底层 Optional 内容。

【问题讨论】:

  • Optional 不应用于属性或参数,仅用于返回类型。

标签: java json serialization gson


【解决方案1】:

经过几个小时的搜索和编码 - 这是我的版本:

public class OptionalTypeAdapter<E> extends TypeAdapter<Optional<E>> {

    public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            Class<T> rawType = (Class<T>) type.getRawType();
            if (rawType != Optional.class) {
                return null;
            }
            final ParameterizedType parameterizedType = (ParameterizedType) type.getType();
            final Type actualType = parameterizedType.getActualTypeArguments()[0];
            final TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(actualType));
            return new OptionalTypeAdapter(adapter);
        }
    };
    private final TypeAdapter<E> adapter;

    public OptionalTypeAdapter(TypeAdapter<E> adapter) {

        this.adapter = adapter;
    }

    @Override
    public void write(JsonWriter out, Optional<E> value) throws IOException {
        if(value.isPresent()){
            adapter.write(out, value.get());
        } else {
            out.nullValue();
        }
    }

    @Override
    public Optional<E> read(JsonReader in) throws IOException {
        final JsonToken peek = in.peek();
        if(peek != JsonToken.NULL){
            return Optional.ofNullable(adapter.read(in));
        }

        in.nextNull();
        return Optional.empty();
    }

}

您可以像这样使用GsonBuilder 简单地注册它:

instance.registerTypeAdapterFactory(OptionalTypeAdapter.FACTORY)

请注意,如果字段不存在于 json 中,Gson 不会为您的类字段设置值。所以你需要在你的实体中设置默认值Optional.empty()

【讨论】:

【解决方案2】:

Ilya 的解决方案忽略了类型参数,因此在一般情况下它不能真正起作用。我的解决方案相当复杂,因为需要区分 nullOptional.absent() - 否则您可以将封装剥离为列表。

public class GsonOptionalDeserializer<T>
implements JsonSerializer<Optional<T>>, JsonDeserializer<Optional<T>> {

    @Override
    public Optional<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        final JsonArray asJsonArray = json.getAsJsonArray();
        final JsonElement jsonElement = asJsonArray.get(0);
        final T value = context.deserialize(jsonElement, ((ParameterizedType) typeOfT).getActualTypeArguments()[0]);
        return Optional.fromNullable(value);
    }

    @Override
    public JsonElement serialize(Optional<T> src, Type typeOfSrc, JsonSerializationContext context) {
        final JsonElement element = context.serialize(src.orNull());
        final JsonArray result = new JsonArray();
        result.add(element);
        return result;
    }
}

【讨论】:

  • 您是否需要为要序列化的每种Optional 注册一个新的GsonOptionalDeserializer 实例?
  • @scompt.com 不,就像here
  • 啊,我明白了。我最终实现了TypeAdapterFactory,并做了类似于Javadocs 中Multiset example 的事情。
  • 看起来在反序列化期间,这仍然无法区分Optional.absent()null,因为它在所有情况下都返回非空Optional
  • @JackEdmonds 确实不能,因为它需要 Optional.absent()null 的不同表示。您可以通过使用单元素数组来解决它(因为Optional 是一个最多一个元素的集合),但是OTOH,Optional 只不过是爆炸@Nullable 并且拥有@Nullable Optional 很简单错了。
【解决方案3】:

作为对maaartinus解决方案的补充,没有封装列表的版本,其中Optional.absent被简单地序列化为null

public class GsonOptionalDeserializer<T> implements JsonSerializer<Optional<T>>, JsonDeserializer<Optional<T>> {
    @Override
    public Optional<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
        final T value = context.deserialize(json, ((ParameterizedType) typeOfT).getActualTypeArguments()[0]);
        return Optional.fromNullable(value);
    }

    @Override
    public JsonElement serialize(Optional<T> src, Type typeOfSrc, JsonSerializationContext context) {
        return context.serialize(src.orNull());
    }
}

【讨论】:

  • 这比答案好。 JsonArray 不是必需的。请注意,这个答案是关于番石榴选项的。对于 Java8 选项,请使用 Optional.ofNullable(value)src.orElse(null)
【解决方案4】:

我会添加到Anton Onikiychuk的答案

@Override
public Optional<E> read(JsonReader in) throws IOException {
    JsonToken peek = in.peek();
    if (peek == JsonToken.NULL) {
        in.nextNull(); // consuming JSON null
        return Optional.empty();
    }

    return Optional.ofNullable(adapter.read(in));
}

【讨论】:

  • 这是一个很好且重要的观点!尽管将其添加为评论或建议将其作为编辑可能会更好。我现在提出了一个编辑来解决这个问题。
猜你喜欢
  • 2013-06-18
  • 1970-01-01
  • 2015-05-06
  • 2013-02-01
  • 1970-01-01
  • 2021-11-28
  • 1970-01-01
  • 2018-07-20
  • 1970-01-01
相关资源
最近更新 更多