这是我提出的解决方案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]