2014-02-23 42 views
3

我有Multiset<String>对象,我将其序列化为Json。我做这个使用GSON如下:Json Multiset vs ArrayList

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,然后使用构造函数来获得我的multiset,但它似乎是一个迂回的方式。是否有更直接的反序列化json对象来检索我的多集?

回答

3

编辑:它看起来像在这种情况下,有一个更容易的解决这个问题,即通过注册多集的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(); 

的实例创建者只定义一个多重应该如何创建,因为Guava集合没有默认的构造函数(无论如何,Multiset都是接口)。

原来的答复:我不知道这是否是达到你想要什么是最好的或最简单的方法,但它是最近对我们工作了一个类似的问题的一种方法(在我们的情况下,它是deserialising到一个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); 
    } 
} 

总之,这蒙上预期的类型deserialise到(例如,在你的情况new TypeToken<Multiset<String>>(){}.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,之后立即抛弃它。

+0

这实际上就是我所做的(虽然代码不够整齐)。 –

+0

@ChthonicProject查看我的更新答案,事实证明这比我想象的要容易得多,因为你需要做的就是定义如何创建一个Multiset。 – andersschuller