2015-04-05 59 views
0

這裏的String數組的地圖是完整的錯誤我收到:IllegalStateException異常 - 序列化使用GSON

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 3 path $. 

我想編碼的地圖是

HashMap<String[],Boolean>. 

我用這個來序列化地圖:

Gson gson = new Gson(); 
gson.toJson(object); 

,這反序列化對象:

json.fromJson(encodedObject, new TypeToken<HashMap<String[], Boolean>>(){}.getType()) 

這是原始的JSON輸出:

{ 
    "[Ljava.lang.String;@433fe238":false, 
    "[Ljava.lang.String;@433feb38":false, 
    "[Ljava.lang.String;@433fe018":false, 
    "[Ljava.lang.String;@433fd618":false 
} 

我有我以類似的方式編碼和解碼等地圖,但是這是給我的問題只有一個。有沒有辦法做到這一點?

+0

我不能幫你解決Gson問題,因爲我沒有經驗,但是我確實看到了一個你可能想要解決的關鍵問題。一個數組不能很好地用作'HashMap'鍵。數組沒有一個表現良好的'hashCode()'和'equals()'。使用'列表'。 – 2015-04-05 23:32:39

+0

無論如何,我谷歌搜索和你的問題似乎是這樣的:http://stackoverflow.com/questions/25522309/converting-json-between-string-and-byte-with-gson – 2015-04-05 23:34:14

+0

謝謝你的擡頭。這是我正在嘗試修復的舊代碼,如果沒有某些問題,我無法真正更改數據結構。當我編寫它時,我有點新手:) – eBehbahani 2015-04-05 23:35:13

回答

2

這是不幸的,你不能改變的數據結構件的類型。您應該永遠不會將數組存儲爲鍵,因爲它們不會覆蓋equals,hashcode和toString。

那就說讓我們來談談你的錯誤。這是由於Gson將密鑰序列化爲字符串,因爲JSON語法要求在鍵值對中密鑰必須是字符串。但是你期望一個數組作爲關鍵字,因此解析器會拋出錯誤。

由於不能改變結構,可以編寫自定義串行器和解串來處理這個問題:

class MapSerializer implements JsonSerializer<Map<String[], Boolean>> { 
    @Override 
    public JsonElement serialize(Map<String[], Boolean> src, Type typeOfSrc, JsonSerializationContext context) { 
     JsonObject obj = new JsonObject(); 
     for(Map.Entry<String[], Boolean> entry : src.entrySet()) { 
      obj.addProperty(Arrays.toString(entry.getKey()), entry.getValue()); 
     } 
     return obj; 
    } 
} 

class MapDeserializer implements JsonDeserializer<Map<String[], Boolean>> { 
    @Override 
    public Map<String[], Boolean> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 
     Map<String[],Boolean> map = new HashMap<>(); 
     JsonObject obj = json.getAsJsonObject(); 
     for(Map.Entry<String, JsonElement> entry : obj.entrySet()) { 
      String s = entry.getKey(); 
      //See how you're dependent of the String representation given by Arrays.toString? 
      //Not very convenient. 
      map.put(s.substring(1, s.length()-1).split(", "), entry.getValue().getAsBoolean()); 
     } 
     return map; 
    } 
} 

基本上鍵由Arrays.toString,這是陣列中的元件的可讀表示現在給出,而不是其toString方法。

但是,正如你所看到的,反序列化過程必須假設這種表示不會改變,這是相當危險的,因爲Arrays.toString的結果中的任何修改都會打破這個過程,但是你必須處理它...(雖然這不太可能發生,但這是一個事實,你必須知道)

爲了使這項工作,您需要在解析器中註冊適配器。

Map<String[],Boolean> map = new HashMap<>(); 
map.put(new String[]{"Hello"},false); 
map.put(new String[]{"Stack", "Overflow"},true); 

Type t = new TypeToken<Map<String[], Boolean>>(){}.getType(); 

Gson gson = new GsonBuilder().registerTypeAdapter(t, new MapSerializer()) 
          .registerTypeAdapter(t, new MapDeserializer()) 
          .setPrettyPrinting() 
          .create(); 

String repr = gson.toJson(map, t); 
Map<String[], Boolean> map2 = gson.fromJson(repr, t); 

如果您嘗試打印repr,你會得到:

{ 
    "[Hello]": false, 
    "[Stack, Overflow]": true 
} 

比你原來的輸出更好。你回到與fromJson(我引用「相同」,因爲按鍵是等於根據Arrays.equals而不是.equals())相同的地圖。

希望它有幫助!

+0

所以我確實玩過這一點,並且實際上最終改變了結構,從地圖到列表。這似乎工作,即使列表仍然包含字符串數組。 – eBehbahani 2015-04-06 14:54:07

+0

@rioneye如果使用Map更方便,你可以在數組周圍創建一個包裝類,用'Arrays.toString'覆蓋toString,用'Arrays.hashCode'代替hashcode,用Arrays.equals代替equals。 – 2015-04-06 14:58:58

+0

@rioneye然後你只需編寫反序列化器,你就可以控制包裝器的'toString'實現。而'map.equals(map2)'當然會給你真實的。如果需要,你可以在那裏找到一個實現:https://gist.github.com/alexcrt/3585ca1916a426a816c7 – 2015-04-06 15:05:49

0

也許你應該寫一個toString()方法String[],或者你只會得到的每一個地址的String []