如何使用Gson反序列化ConcurrentMap

时间:2013-09-17 18:04:16

标签: java json gson

我正在尝试对其中包含ConcurrentMap的对象进行反序列化,但是我得到了一个异常。

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);
}

如何让它正确地反序列化地图?我需要提供自定义反序列化器吗?

请注意,我在同一个班级中有常规MapsConcurrentMaps。我也是序列化类的人,所以,如果我可以提供一个自定义序列化程序来指定并发映射的类型,然后是一个反序列化程序,它指定应该如何创建它们作为应该工作的对象。

更新:我反序列化的类看起来像这样:

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();
    ...
}

1 个答案:

答案 0 :(得分:-1)

这是我提出的TypeAdapter解决方案。在向您展示代码之前,我必须说如果字段存在于解析后的字符串中,final不应该用于字段,因为Gson会尝试为该字段赋值。

我已更改Row课程,仅用于展示解决方案。

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]