2013-09-17 4 views
2

Я пытаюсь десериализовать объект, в котором есть ConcurrentMap, но я получаю исключение.Как десериализировать ConcurrentMap с Gson

Caused by: java.lang.IllegalArgumentException: Can not set java.util.concurrent.ConcurrentMap field com.example.Row.doubleValues to java.util.LinkedHashMap 

Моя функция выглядит примерно так:

T deserialise(final InputStream input, final Class<T> type) { 
    GsonBuilder gsonBuilder = new GsonBuilder(); 
    Gson gson = gsonBuilder.create(); 
    InputStreamReader isr = new InputStreamReader(input); 
    return gson.fromJson(isr, type); 
} 

Как заставить его десериализацию карты правильно? Нужно ли мне предоставлять собственный десериализатор?

Обратите внимание, что у меня есть как обычные Maps, так и ConcurrentMaps в одном классе. Я также являюсь тем, кто сериализует класс, поэтому, если я могу предоставить собственный сериализатор, который задает тип параллельных карт, а затем десериализатор, который определяет, как они должны создаваться как объекты, которые должны работать.

Update: Класс, который я десериализации выглядит так:

public class Row implements Serializable { 
    private static final long serialVersionUID = 1L; //there's a diff value here 

    private final String key; 
    private final ConcurrentMap<String, Double> doubleValues = Maps.newConcurrentMap(); 
    private Map<String, String> writeOnceValues = Maps.newHashMap(); 
    ... 
} 
+0

могли бы вы показать соответствующую часть класса десериализации в? – Katona

+0

@ Katona добавил пример – naumcho

+0

одним очевидным решением является изменение типа 'doubleValues' с' ConcurrentMap' на 'Map', вам это действительно нужно? В противном случае вам понадобится индивидуальная десериализация, см. [This] (http://stackoverflow.com/questions/16127904/gson-fromjson-return-linkedhashmap-instead-of-enummap) вопрос, проблема аналогична, но не является indentical, и я думаю это решение ('InstanceCreator') может работать здесь. – Katona

ответ

-1

Это мой предложенное решение с TypeAdapter. Прежде чем показывать вам код, я должен сказать, что final не следует использовать для поля, если поле присутствует в анализируемой строке, так как Gson попытается присвоить значение этому полю.

Мой измененный класс, только для демонстрации решения.

public class Row implements Serializable { 
    private static final long serialVersionUID = 1L; //there's a diff value here 

    private final String key = "myKey"; 
    private ConcurrentMap<String, Double> doubleValues = Maps.newConcurrentMap(); 
    private Map<String, String> writeOnceValues = Maps.newHashMap(); 

    @Override 
    public String toString() { 
     return "Row [key=" + key.getClass() + ", doubleValues=" + doubleValues.getClass() 
       + ", writeOnceValues=" + writeOnceValues.getClass() + "]"; 
    } 

} 

и адаптер типа, где я в основном использовать стандартные синтаксический, но возвращаю ConcurrentHashMap.

public final class ConcurrentHashMapTypeAdapter<K,V> extends TypeAdapter<ConcurrentMap<K,V>> { 

    @Override 
    public synchronized ConcurrentMap<K,V> read(JsonReader in) throws IOException { 
    if (in.peek() == JsonToken.NULL) { 
     in.nextNull(); 
     return null; 
    } 
    Type aType = new TypeToken<LinkedTreeMap<K,V>>() {}.getType(); 
    Gson g = new Gson(); 
    LinkedTreeMap<K,V> ltm = g.fromJson(in, aType); 
    return new ConcurrentHashMap<K,V>(ltm); 
    } 

    @Override 
    public synchronized void write(JsonWriter out, ConcurrentMap<K, V> value) throws IOException { 
    Gson g = new Gson(); 
    out.value(g.toJson(value)); 
    } 
} 

и метод испытаний, где я покажу вам, что он работает.

public static void main(String[] args) { 
    Gson simpleGson = new Gson(); 

    // deserialize row once to get a suitable JSON string 
    String s = simpleGson.toJson(new Row()); 
    System.out.println("JSON string: " + s); 

    Row r; 
    try{ 
     r = simpleGson.fromJson(s, Row.class); 
    } catch(Exception e){ 
     System.out.println("Oh no, assignment is not possible"); 
     e.printStackTrace(); 
    } 


    GsonBuilder gb = new GsonBuilder(); 
    Type aType = new TypeToken<ConcurrentMap<String,Double>>() {}.getType(); 
    gb.registerTypeAdapter(aType, new ConcurrentHashMapTypeAdapter<String, Double>()); 

    Gson gsonWithTypeAdapter = gb.create(); 

    r = gsonWithTypeAdapter.fromJson(s, Row.class); 
    System.out.println("Now it works"); 
    System.out.println("Row: " +r); 
} 

Это мое исполнение:

JSON string: {"key":"myKey","doubleValues":{},"writeOnceValues":{}} 
Oh no, assignment is not possible 
java.lang.IllegalArgumentException: Can not set java.util.concurrent.ConcurrentMap field stackoverflow.questions.q18856793.Row.doubleValues to com.google.gson.internal.LinkedTreeMap 
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source) 
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source) 
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(Unknown Source) 
    at java.lang.reflect.Field.set(Unknown Source) 
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:95) 
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172) 
    at com.google.gson.Gson.fromJson(Gson.java:803) 
    at com.google.gson.Gson.fromJson(Gson.java:768) 
    at com.google.gson.Gson.fromJson(Gson.java:717) 
    at com.google.gson.Gson.fromJson(Gson.java:689) 
    at stackoverflow.questions.q18856793.Q18856793.main(Q18856793.java:23) 
Now it works 
Row: Row [key=class java.lang.String, doubleValues=class java.util.concurrent.ConcurrentHashMap, writeOnceValues=class com.google.gson.internal.LinkedTreeMap] 
+0

Какова цель 'TypeAdapterFactory FACTORY' в классе' ConcurrentHashMapTypeAdapter', если она не используется нигде в примере? – andrybak

+0

'TypeAdapter <...>' механизм в Gson специально разработан для предоставления информации о типах, где он недоступен из-за стирания стилей в Java. Генерируемый 'TypeAdapter' просто ошибочен. – andrybak

+0

@andrybak было 3 года. Дайте мне хотя бы 1 день, чтобы проверить ответ. – giampaolo

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