我有Multiset<String>对象,我将其序列化为Json。我正在使用Gson进行以下操作:

Multiset<String> mset = ... ;
Gson gson = new Gson();
Files.write(Gson.toJson(mset), new File(abosulte_path_string), Charset.defaultCharset());

当我尝试对其进行反序列化时,请执行以下操作:
String json_string = ... // read from file
Type type = new TypeToken<Multiset<String>>(){}.getType();
Multiset<String> treated = gson.fromJson(json_string, type);

我收到此错误:
java.lang.ClassCastException: java.util.ArrayList cannot be cast to com.google.common.collect.Multiset

当我打开json文件时,我看到Multiset<String>对象确实被表示为ArrayList([string1,string2,...]),并重复了多集中具有count > 1的字符串。

我当然可以将其转换为ArrayList,然后使用create(Iterable<>)构造函数获取我的多重集,但这似乎是一种回旋方式。有没有更直接的方法来反序列化json对象以检索我的多集?

最佳答案

编辑:在这种情况下,似乎有一种更简单的方法来解决此问题,即通过为多集注册instance creator:

private static class MultisetInstanceCreator implements InstanceCreator<Multiset<?>> {
    @Override
    public Multiset<?> createInstance(Type type) {
        return HashMultiset.create();
    }
}

Gson gson = new GsonBuilder()
        .registerTypeAdapter(Multiset.class, new MultisetInstanceCreator())
        .create();

实例创建者只是定义了应如何创建Multiset,因为Guava集合没有默认构造函数(而且Multiset仍然是接口)。

原始答案:我不确定这是实现所需目标的最佳还是最简单的方法,但这是最近对我们解决类似问题的一种方法(在我们的情况下,这是反序列化为ImmutableMap)。

基本思想是注册一个custom deserialiser,它基本上会执行您已经发现的可能的解决方案:将序列反序列化为ArrayList,然后将其转换为Multiset。这样做的好处是,您只需注册一次自定义反序列化器,而无需了解任何地方即可首先使用ArrayList类型。

此自定义解串器如下所示:
private static class MultisetDeserializer implements JsonDeserializer<Multiset<?>> {
    @Override
    public Multiset<?> deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
        ParameterizedType parameterizedType = (ParameterizedType) type;
        Type[] typeArguments = parameterizedType.getActualTypeArguments();

        ParameterizedType listType = new ListParameterizedType(typeArguments);
        List<?> list = context.deserialize(json, listType);

        return HashMultiset.create(list);
    }
}

简而言之,这会将要反序列化的预期类型(例如您的情况下的new TypeToken<Multiset<String>>(){}.getType())强制转换为 ParameterizedType 以获取类型参数(您的示例中为String)。然后,它创建一个新的ParameterizedType,它是具有相同类型参数的ArrayList的类型(如下所示)。使用上下文将JSON反序列化为这种新类型后,您要做的就是调用HashMultiset.create
ListParameterizedType看起来像这样:
private static class ListParameterizedType implements ParameterizedType {
    private final Type[] typeArguments;

    private ListParameterizedType(Type[] typeArguments) {
        this.typeArguments = typeArguments;
    }

    @Override
    public Type[] getActualTypeArguments() {
        return typeArguments;
    }

    @Override
    public Type getRawType() {
        return ArrayList.class;
    }

    @Override
    public Type getOwnerType() {
        return null;
    }
}

请注意,您可以在这里用几乎任何列表类替换ArrayList,只要它具有一个类型实参和一个默认构造函数即可。

也可能有更简单的方法来实现相同的目的,例如,您可以通过使用JsonElement之类的方法检查isJsonArray()来手动进行一些解析。这样可以避免创建ArrayList,然后立即丢弃它。

10-07 12:22