3

我有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 对象以检索我的多重集?

4

1 回答 1

3

编辑:在这种情况下,似乎有一种更简单的方法可以解决这个问题,即为 Multisets 注册一个实例创建者

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)。

基本思想是注册一个自定义反序列化器,它基本上可以完成您已经发现的可能解决方案:反序列化为 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 之后立即丢弃。

于 2014-02-23T19:53:49.107 回答