这是一个狭隘的例子,因此没有意义,但证明了这个问题。
以下代码:
import java.util.*;
import com.google.gson.*;
class X {}
class SomeType {
private Map <X, String> map;
public SomeType() {
this.map = new HashMap<X, String>();
map.put(new X(), "b");
}
}
public class FooMain {
private static Gson gson = new GsonBuilder().serializeNulls().create();
public static void main(String args[]) throws Exception {
String foo = gson.toJson(new SomeType(), SomeType.class);
System.out.println(foo); // line 20
SomeType st = gson.fromJson(foo, SomeType.class); // line 21
}
}
失败了:
[java] Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 20
[java] at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176)
[java] at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
[java] at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:187)
[java] at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:146)
[java] at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93)
[java] at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172)
[java] at com.google.gson.Gson.fromJson(Gson.java:795)
[java] at com.google.gson.Gson.fromJson(Gson.java:761)
[java] at com.google.gson.Gson.fromJson(Gson.java:710)
[java] at com.google.gson.Gson.fromJson(Gson.java:682)
[java] at FooMain.main(FooMain.java:21)
[java] Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 20
[java] at com.google.gson.stream.JsonReader.expect(JsonReader.java:339)
[java] at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:322)
[java] at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:165)
[java] ... 10 more
而第20行打印:
{"map":{"X@185b10b":"b"}}
答案 0 :(得分:2)
Gson
使用键作为JSON键并将map元素的值作为值来序列化Map
。由于您的X
类没有自定义覆盖toString()
方法,因此它使用Object#toString()
并将其序列化为X@185b10b
,但无法对其进行反序列化。即使你确实提供了toString()
,它实际上也无法反序列化。
我猜你发现了一个边缘情况,你无法正确序列化所有内容。 JSON对象的键必须是String
。
答案 1 :(得分:0)
好的,感谢Sotirios Delimanolis的回答,我找到了以下内容:
您只能为Map
密钥类提供自定义反序列化程序。在这种情况下,该密钥类的toString
方法必须提供&#34;序列化&#34;格式
或
您可以为Map
类本身提供通用适配器(序列化器+解串器),以及Map
密钥类的通用适配器(序列化器+解串器)。这种方法的唯一优点是,密钥类的toString
方法可以用于其他目的,并且不必与解串器一起使用。
以下是第二种情况的完整代码:
import java.util.*;
import com.google.gson.*;
import java.lang.reflect.Type;
import org.apache.commons.lang3.StringUtils;
class X {
public int x;
public X(int x) {
this.x = x;
}
public String toString() {
return String.format("boo ha ha %d", x);
}
}
class SomeType {
private Map <X, String> map;
public SomeType() {
this.map = new HashMap<X, String>();
map.put(new X(2), "b");
}
public String toString() {
List<String> rv = new ArrayList<>();
for (X x : map.keySet())
rv.add(String.format("%s -> %s\n", x.toString(), map.get(x)));
return StringUtils.join(rv, "\n");
}
}
public class FooMain {
public static void main(String args[]) throws Exception {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(X.class, new XAdapter());
gsonBuilder.registerTypeAdapter(Map.class, new MapAdapter());
Gson gson = gsonBuilder.serializeNulls().create();
SomeType original = new SomeType();
System.out.println("original is: "+original);
String foo = gson.toJson(original, SomeType.class);
System.out.println("JSON form is: "+foo);
SomeType reconstructed = gson.fromJson(foo, SomeType.class);
System.out.println("reconstructed is: "+reconstructed);
}
}
class MapAdapter implements JsonSerializer<Map<?, ?>>, JsonDeserializer<Map<?, ?>> {
@Override
public JsonElement serialize(Map<?, ?> m, Type typeOfT, JsonSerializationContext context) {
JsonArray rv = new JsonArray();
for (Object k : m.keySet()) {
JsonObject kv = new JsonObject();
kv.add ("k" , context.serialize(k));
kv.addProperty("ktype" , k.getClass().getName());
kv.add ("v" , context.serialize(m.get(k)));
kv.addProperty("vtype" , m.get(k).getClass().getName());
rv.add(kv);
}
return rv;
}
@Override
public Map<?, ?> deserialize(JsonElement _json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonArray json = (JsonArray) _json;
Map<Object, Object> rv = new HashMap<>();
for (int i = 0 ; i < json.size() ; i++) {
JsonObject o = (JsonObject) json.get(i);
String ktype = o.getAsJsonPrimitive("ktype").getAsString();
String vtype = o.getAsJsonPrimitive("vtype").getAsString();
Class<?> kklass = null;
Class<?> vklass = null;
try {
kklass = Class.forName(ktype);
vklass = Class.forName(vtype);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new JsonParseException(e.getMessage());
}
Object k = context.deserialize( o.get("k"), kklass);
Object v = context.deserialize( o.get("v"), vklass);
rv.put(k, v);
}
return rv;
}
}
class XAdapter implements JsonSerializer<X>, JsonDeserializer<X> {
@Override
public JsonElement serialize(X x, Type typeOfT, JsonSerializationContext context) {
return context.serialize(x.x);
}
@Override
public X deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String s = json.getAsString();
int x = Integer.valueOf(s);
return new X(x);
}
}