Json Multiset与ArrayList

时间:2014-02-23 19:10:57

标签: java json arraylist multiset

我有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,...]),并且重复了{{1}的字符串在multiset中。

当然,我可以将它转换为ArrayList,然后使用count > 1构造函数来获取我的multiset,但它似乎是一种迂回的方式。是否有更直接的方法来反序列化json对象以检索我的多重集?

1 个答案:

答案 0 :(得分:3)

编辑:在这种情况下,有一种更简单的方法可以解决这个问题,即为Multisets注册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。