首页 > 解决方案 > 使用gson序列化具有泛型类型的类?

问题描述

我有以下课程

private static class ClassWithGenericType<T> {
    Set<T> values;
}

如果我现在用 - 值初始化类,Set使用Enumgson 序列化和反序列化对象,Set反序列化对象的 不包含Enum- 值,但值 as String

我认为这是因为泛型类型通过序列化被丢弃了。我看到了,我可以使用new TypeToken<...>(){}.getType();,但问题是,上面的类是更大对象的一部分,所以我不能gson.fromJson(classWithGenericType, typeToken)直接调用。

有解决这个问题的聪明方法吗?我想到了 a TypeAdapter,它不仅序列化了 的值Set,而且还序列化了它的类型。

标签: javagson

解决方案


我现在找到了一个解决方案并创建了一个TypeAdapter.

public class SetTypeAdapterFactory implements TypeAdapterFactory {
    @Override
    public <T> TypeAdapter<T> create(Gson gson, @NonNull TypeToken<T> type) {
        if (!Set.class.isAssignableFrom(type.getRawType())) {
            return null;
        }
        return (TypeAdapter<T>) new SetTypeAdapter(gson);
    }
}

public class SetTypeAdapter extends TypeAdapter<Set<?>> {
    public static final String TYPE = "@type";
    public static final String DATA = "@data";
    private final Gson gson;

    public SetTypeAdapter(@NonNull Gson gson) {
        this.gson = gson;
    }

    @Override
    public void write(final JsonWriter out, final Set<?> set
    ) throws IOException {
        out.beginArray();
        for (Object item : set) {
            out.beginObject();
            out.name(TYPE).value(item.getClass().getName());
            out.name(DATA).jsonValue(gson.toJson(item));
            out.endObject();
        }
        out.endArray();
    }

    @Override
    public Set<?> read(final JsonReader in) throws IOException {
        final Set<Object> set = Sets.newHashSet();
        in.beginArray();
        while (in.hasNext()) {
            in.beginObject();
            set.add(readNextObject(in));
            in.endObject();
        }
        in.endArray();
        return set;
    }

    private Object readNextObject(JsonReader in) throws IOException {
        try {
            checkNextName(in, TYPE);
            Class<?> cls = Class.forName(in.nextString());
            checkNextName(in, DATA);
            return gson.fromJson(in, cls);
        } catch (ClassNotFoundException exception) {
            throw new IOException(exception);
        }
    }

    private void checkNextName(JsonReader in, String name) throws IOException {
        if (!in.nextName().equals(name)) {
            throw new IOException("Name was not: " + name);
        }
    }
}

我们可以将工厂添加到 中GsonBuilder,然后我们可以Set使用泛型类型序列化 a。

var gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapterFactory(new SetTypeAdapterFactory());
var gson = gsonBuilder.create();

序列化Set后的结构如下:

[
  {
    "@type":<class_name_first_element>,
    "@data":<first_element_as_json>
  }, 
  ...
]

推荐阅读