I get the error:
我收到错误:
Exception in thread "main" com.google.gson.JsonParseException:
Expecting object found: "com.shagie.app.SimpleMap$Data@24a37368"
when trying to deseralize a Map that uses non-trivial keys:
尝试解除使用非平凡键的Map时:
package com.shagie.app;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.HashMap;
public class SimpleMap {
public static void main(String[] args) {
Wrapper w = new Wrapper();
w.m.put(new Data("f", 1), new Data("foo", 3));
w.m.put(new Data("b", 2), new Data("bar", 4));
GsonBuilder gb = new GsonBuilder();
gb.setPrettyPrinting();
Gson g = gb.create();
String json = g.toJson(w);
System.out.println(json);
w = g.fromJson(json, Wrapper.class);
System.out.println(w.m.isEmpty());
}
static public class Wrapper {
HashMap<Data, Data> m = new HashMap<Data, Data>();
}
static public class Data {
String s;
Integer i;
public Data(String arg, Integer val) { s = arg; i = val; }
}
}
This serializes to the json:
这序列化为json:
{ "m": { "com.shagie.app.SimpleMap$Data@24a37368": { "s": "foo", "i": 3 }, "com.shagie.app.SimpleMap$Data@66edc3a2": { "s": "bar", "i": 4 } } }
One can see the key attempting to be serialized, but certainly not in a way that can be deserialized.
可以看到尝试序列化的密钥,但肯定不是可以反序列化的。
How does one serialize this object so that it can be deserialized?
如何序列化此对象以便可以反序列化?
3 个解决方案
#1
8
I found the following while trying to solve this puzzle: Issue 210: Cannot serialize or deserialize Maps with complex keys.
我在尝试解决这个难题时发现了以下内容:问题210:无法使用复杂键序列化或反序列化地图。
For any internet travelers from the future (like myself)... you can enable this functionality in GSON 2.* with the enableComplexMapKeySerialization() method on GsonBuilder.
对于未来的任何互联网旅行者(如我自己)...您可以使用GsonBuilder上的enableComplexMapKeySerialization()方法在GSON 2. *中启用此功能。
Here's the javadoc for that method.
这是该方法的javadoc。
When enabled, the map will be serialized (and correctly deserialized) as an array of [key, value] arrays:
启用后,映射将作为[key,value]数组的数组序列化(并正确反序列化):
{"m":[[{"s":"f", "i",1}, {"s":"foo", "i":3}], [{"s":"b", "i",2}, {"s":"bar", "i":4}]]}
#2
2
The problem is that toString()
is getting called on the keys to the map, rather than them being serialized themselves.
问题是toString()被调用到地图的键上,而不是自己被序列化。
To fix this a custom serializer and deserializer needs to be set up, and the deserializer needs to be aware of the format that the object uses to display itself as a string (the toString()
method must return a string that can be used to reconstruct the entire object).
要解决此问题,需要设置自定义序列化程序和反序列化程序,并且反序列化程序需要知道对象用于将自身显示为字符串的格式(toString()方法必须返回可用于重构的字符串整个对象)。
For the above example:
对于上面的例子:
package com.shagie.app;
import com.google.gson.*;
import java.lang.reflect.Type;
import java.util.HashMap;
public class SimpleMapFixed {
public static void main(String[] args) {
Wrapper w = new Wrapper();
w.m.put(new Data("f", 1), new Data("foo", 3));
w.m.put(new Data("b", 2), new Data("bar", 4));
GsonBuilder gb = new GsonBuilder();
gb.setPrettyPrinting();
gb.registerTypeAdapter(Data.class, new DataSerializer());
Gson g = gb.create();
String json = g.toJson(w);
System.out.println(json);
w = g.fromJson(json, Wrapper.class);
System.out.println(w.m.isEmpty());
}
static public class Wrapper {
HashMap<Data, Data> m = new HashMap<Data, Data>();
}
static public class DataSerializer implements JsonSerializer<Data>,
JsonDeserializer<Data> {
@Override
public Data deserialize(JsonElement je, Type t, JsonDeserializationContext ctx)
throws JsonParseException {
Data rv;
JsonObject jo;
System.out.println("deserialize called with: " + je.toString());
if (je.isJsonObject()) {
jo = je.getAsJsonObject();
rv = new Data(jo.get("s").getAsString(), jo.get("i").getAsInt());
} else {
String js = je.getAsString();
String[] s = js.split(":", 2); // split into two (and only two)
rv = new Data(s[1], Integer.valueOf(s[0]));
}
System.out.println("deserialize returns: " + rv.s + " " + rv.i);
return rv;
}
@Override
public JsonElement serialize(Data data, Type type, JsonSerializationContext jsonSerializationContext) {
JsonObject jo = new JsonObject();
jo.addProperty("s", data.s);
jo.addProperty("i", data.i);
System.out.println("serialize called: " + jo.toString());
return jo;
}
}
static public class Data {
String s;
Integer i;
public Data(String arg, Integer val) { s = arg; i = val; }
@Override
public String toString() {
String rv = i.toString() + ':' + s;
System.out.println("toString called: " + rv);
return rv;
}
}
}
Running this code produces:
运行此代码会产生:
serialize called: {"s":"foo","i":3} toString called: 1:f serialize called: {"s":"bar","i":4} toString called: 2:b { "m": { "1:f": { "s": "foo", "i": 3 }, "2:b": { "s": "bar", "i": 4 } } } deserialize called with: "1:f" deserialize returns: f 1 deserialize called with: {"s":"foo","i":3} deserialize returns: foo 3 deserialize called with: "2:b" deserialize returns: b 2 deserialize called with: {"s":"bar","i":4} deserialize returns: bar 4
Note the invocations of toString()
as part of the serialization. In this code, the logic for the deserializion from the String form is in the DataSerializer
, though it may make sense to move it into the Data
class as another constructor instead - it doesn't affect the final outcome.
请注意toString()的调用作为序列化的一部分。在此代码中,来自String表单的反序列化的逻辑在DataSerializer中,尽管将其作为另一个构造函数移动到Data类中可能是有意义的 - 它不会影响最终结果。
Further note that Data
was a rather simple object itself with no deeper structures. Trying to serialize that as the key would require additional work.
进一步注意,Data本身是一个相当简单的对象,没有更深层的结构。试图将其序列化为密钥需要额外的工作。
#3
0
Its Up to you how you are maintaining the HahMap Keys, You can deserialized it with simple and easiest way.
它取决于您如何维护HahMap键,您可以使用简单,最简单的方法对其进行反序列化。
final Type typeOf = new TypeToken <Map<String, Map<String, Data>>>(){}.getType();
final Map<String, Map<String, Data>> newMap = gson.fromJson(json, typeOf);
final Map<String, Data> map = newMap.get("m");
final Iterator<Entry<String, Data>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Data> pair = (Map.Entry<String,Data>) it.next();
String key = pair.getKey();
System.out.println("key "+ key + " Values[ i= " + data.getI() + ", s= " +data.getS()+" ]");
}
Result:
key = snippet.Snippet$Data@61506150 Values [ i= 3, s= foo ]
key = snippet.Snippet$Data@61506150值[i = 3,s = foo]
key = snippet.Snippet$Data@63ff63ff Values [ i= 4, s= bar ]
key = snippet.Snippet$Data@63ff63ff值[i = 4,s = bar]
#1
8
I found the following while trying to solve this puzzle: Issue 210: Cannot serialize or deserialize Maps with complex keys.
我在尝试解决这个难题时发现了以下内容:问题210:无法使用复杂键序列化或反序列化地图。
For any internet travelers from the future (like myself)... you can enable this functionality in GSON 2.* with the enableComplexMapKeySerialization() method on GsonBuilder.
对于未来的任何互联网旅行者(如我自己)...您可以使用GsonBuilder上的enableComplexMapKeySerialization()方法在GSON 2. *中启用此功能。
Here's the javadoc for that method.
这是该方法的javadoc。
When enabled, the map will be serialized (and correctly deserialized) as an array of [key, value] arrays:
启用后,映射将作为[key,value]数组的数组序列化(并正确反序列化):
{"m":[[{"s":"f", "i",1}, {"s":"foo", "i":3}], [{"s":"b", "i",2}, {"s":"bar", "i":4}]]}
#2
2
The problem is that toString()
is getting called on the keys to the map, rather than them being serialized themselves.
问题是toString()被调用到地图的键上,而不是自己被序列化。
To fix this a custom serializer and deserializer needs to be set up, and the deserializer needs to be aware of the format that the object uses to display itself as a string (the toString()
method must return a string that can be used to reconstruct the entire object).
要解决此问题,需要设置自定义序列化程序和反序列化程序,并且反序列化程序需要知道对象用于将自身显示为字符串的格式(toString()方法必须返回可用于重构的字符串整个对象)。
For the above example:
对于上面的例子:
package com.shagie.app;
import com.google.gson.*;
import java.lang.reflect.Type;
import java.util.HashMap;
public class SimpleMapFixed {
public static void main(String[] args) {
Wrapper w = new Wrapper();
w.m.put(new Data("f", 1), new Data("foo", 3));
w.m.put(new Data("b", 2), new Data("bar", 4));
GsonBuilder gb = new GsonBuilder();
gb.setPrettyPrinting();
gb.registerTypeAdapter(Data.class, new DataSerializer());
Gson g = gb.create();
String json = g.toJson(w);
System.out.println(json);
w = g.fromJson(json, Wrapper.class);
System.out.println(w.m.isEmpty());
}
static public class Wrapper {
HashMap<Data, Data> m = new HashMap<Data, Data>();
}
static public class DataSerializer implements JsonSerializer<Data>,
JsonDeserializer<Data> {
@Override
public Data deserialize(JsonElement je, Type t, JsonDeserializationContext ctx)
throws JsonParseException {
Data rv;
JsonObject jo;
System.out.println("deserialize called with: " + je.toString());
if (je.isJsonObject()) {
jo = je.getAsJsonObject();
rv = new Data(jo.get("s").getAsString(), jo.get("i").getAsInt());
} else {
String js = je.getAsString();
String[] s = js.split(":", 2); // split into two (and only two)
rv = new Data(s[1], Integer.valueOf(s[0]));
}
System.out.println("deserialize returns: " + rv.s + " " + rv.i);
return rv;
}
@Override
public JsonElement serialize(Data data, Type type, JsonSerializationContext jsonSerializationContext) {
JsonObject jo = new JsonObject();
jo.addProperty("s", data.s);
jo.addProperty("i", data.i);
System.out.println("serialize called: " + jo.toString());
return jo;
}
}
static public class Data {
String s;
Integer i;
public Data(String arg, Integer val) { s = arg; i = val; }
@Override
public String toString() {
String rv = i.toString() + ':' + s;
System.out.println("toString called: " + rv);
return rv;
}
}
}
Running this code produces:
运行此代码会产生:
serialize called: {"s":"foo","i":3} toString called: 1:f serialize called: {"s":"bar","i":4} toString called: 2:b { "m": { "1:f": { "s": "foo", "i": 3 }, "2:b": { "s": "bar", "i": 4 } } } deserialize called with: "1:f" deserialize returns: f 1 deserialize called with: {"s":"foo","i":3} deserialize returns: foo 3 deserialize called with: "2:b" deserialize returns: b 2 deserialize called with: {"s":"bar","i":4} deserialize returns: bar 4
Note the invocations of toString()
as part of the serialization. In this code, the logic for the deserializion from the String form is in the DataSerializer
, though it may make sense to move it into the Data
class as another constructor instead - it doesn't affect the final outcome.
请注意toString()的调用作为序列化的一部分。在此代码中,来自String表单的反序列化的逻辑在DataSerializer中,尽管将其作为另一个构造函数移动到Data类中可能是有意义的 - 它不会影响最终结果。
Further note that Data
was a rather simple object itself with no deeper structures. Trying to serialize that as the key would require additional work.
进一步注意,Data本身是一个相当简单的对象,没有更深层的结构。试图将其序列化为密钥需要额外的工作。
#3
0
Its Up to you how you are maintaining the HahMap Keys, You can deserialized it with simple and easiest way.
它取决于您如何维护HahMap键,您可以使用简单,最简单的方法对其进行反序列化。
final Type typeOf = new TypeToken <Map<String, Map<String, Data>>>(){}.getType();
final Map<String, Map<String, Data>> newMap = gson.fromJson(json, typeOf);
final Map<String, Data> map = newMap.get("m");
final Iterator<Entry<String, Data>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Data> pair = (Map.Entry<String,Data>) it.next();
String key = pair.getKey();
System.out.println("key "+ key + " Values[ i= " + data.getI() + ", s= " +data.getS()+" ]");
}
Result:
key = snippet.Snippet$Data@61506150 Values [ i= 3, s= foo ]
key = snippet.Snippet$Data@61506150值[i = 3,s = foo]
key = snippet.Snippet$Data@63ff63ff Values [ i= 4, s= bar ]
key = snippet.Snippet$Data@63ff63ff值[i = 4,s = bar]