java - 如何使用 Gson 反序列化 ConcurrentMap

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

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

请注意,我在同一类中同时拥有常规 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();
    ...
}

最佳答案

这是我提出的带有 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]

关于java - 如何使用 Gson 反序列化 ConcurrentMap,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18856793/

相关文章:

javascript - 获取日期和/或在 JSON 对象中检查它?

ios - 在 iOS Swift 中解析 JSON 格式的最佳方法是什么?

java - Gson,创建一个简单的 JsonObjects 的 JsonArray

java - ORMLite 数据库模型通过改造适配器转为 GSON 错误; java.lang.IllegalStateException

json - Cloudant NoSql Bluemix 内容类型错误 : bad_content_type

android - kotlinx.serialization : How to parse to different varaiable name than the exact name of JSON key

java - 为什么我的 Java GUI GridBagLayout 没有将 JPanel Grid 放在框架中间?

java - 预先创建的 SQLite 数据库集成到 Android 应用程序中

java - 如何使用 com.google.gwt.user.datepicker.client.DateBox 限制可用的日期范围

java - 批处理文件错误级别问题