2014-02-23 63 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