【问题标题】:Json Multiset vs ArrayListJson Multiset vs ArrayList
【发布时间】:2014-03-25 06:03:47
【问题描述】:

我有 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&lt;String&gt; 对象确实表示为 ArrayList ([string1, string2, ... ]),在多重集中包含 count &gt; 1 的字符串重复.

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

【问题讨论】:

    标签: java json arraylist multiset


    【解决方案1】:

    编辑:在这种情况下,似乎有一种更简单的方法可以解决这个问题,即为 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&lt;Multiset&lt;String&gt;&gt;(){}.getType())转换为 ParameterizedType 以获取类型参数(在您的示例中为字符串)。然后它创建一个新的 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,然后您会立即将其丢弃。

    【讨论】:

    • 这实际上是我所做的(虽然代码不是那么简洁)。
    • @ChthonicProject 看看我更新的答案,事实证明这比我想象的要容易得多,因为你需要做的就是定义如何创建一个 Multiset。
    猜你喜欢
    • 2011-01-25
    • 1970-01-01
    • 1970-01-01
    • 2021-12-19
    • 2011-02-22
    • 2012-08-07
    • 2017-10-20
    • 2013-01-17
    • 2011-10-11
    相关资源
    最近更新 更多