2015-04-05 4 views
0

Вот полная ошибка я получаю: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()'. Используйте «Список '. –

+0

В любом случае, я Googled, и ваша проблема кажется такой же: http://stackoverflow.com/questions/25522309/converting-json-between-string-and-byte-with-gson –

+0

Спасибо за головы. Этот старый код, который я пытаюсь исправить, и я не могу изменить структуру данных без каких-либо проблем. Я был немного новичком, когда я его написал :) – eBehbahani

ответ

2

Это прискорбно вы не можете изменить тип элементов структуры данных. Вероятно, вы никогда не должны хранить массивы в качестве ключей, поскольку они не переопределяют равные, hashcode и toString.

Это говорит о вашей ошибке. Это связано с тем, что Gson сериализует ключ как String, потому что в грамматике 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

+0

@rioneye Если его более удобно использовать карту, вы можете создать класс-оболочку вокруг массива, который переопределяет toString с помощью 'Arrays.toString', hashcode с' Arrays.hashCode' и равен 'Arrays.equals'. –

+0

@rioneye Тогда вам нужно будет написать десериализатор, и вы будете иметь контроль над реализацией оболочки toString. И 'map.equals (map2)' дал бы вам правду, конечно. При необходимости вы можете найти реализацию: https://gist.github.com/alexcrt/3585ca1916a426a816c7 –

0

Может быть, вы должны написать метод toString() для String[], или вы получите только адрес каждой String []

Смежные вопросы