使用GSon反序列化Map<Object, Object>

8

我有一个Map(映射)包括多种类型,就像这个简单的例子一样

final Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("a", 1);
map.put("b", "a");
map.put("c", 2);
final Gson gson = new Gson();
final String string = gson.toJson(map);
final Type type = new TypeToken<LinkedHashMap<String, Object>>(){}.getType();
final Map<Object, Object> map2 = gson.fromJson(string, type);
for (final Entry<Object, Object> entry : map2.entrySet()) {
    System.out.println(entry.getKey() + " : " + entry.getValue());
}

我得到的是普通的Object,没有IntegerString。输出看起来像这样:
a : java.lang.Object@48d19bc8
b : java.lang.Object@394a8cd1
c : java.lang.Object@4d630ab9

有没有什么办法可以修复它?我期望这样简单的情况应该默认正确处理。

我知道关于类型的信息并不总是能够被保留下来,也可能在JSON中1"1"恰好表示相同的含义。然而,返回纯粹没有内容的对象对我来说毫无意义。

更新:序列化版本(即上面的string)看起来很好:

{"a":1,"b":"a","c":2}

你能看一下final String string是什么样子吗?我相信这会有助于理解问题。 - Jean Hominal
我也遇到了这个问题...这就是为什么我使用我的 JSONer...它可能会慢一些,但比 Gson 更通用:http://nu-art-infrastructure.blogspot.co.il/2013/03/jsoner.html - TacB0sS
@TacB0sS:我已经遵循了“清晰和静态的数据结构”的建议。然而,让Gson处理这个应该非常简单。你介意提出一个问题吗? - maaartinus
关于这件事情最糟糕的是,我在Android上使用GSON,在我的手机上它运行良好,而在其他手机上却出现了你所描述的问题... 我的解决方案也很清晰和静态,但在JSONer解析器中,你可以使用带有嵌套复杂对象的映射和列表,所有这些都可以在注释中解决! - TacB0sS
关于什么问题需要提出一个问题?他们不是已经知道这个问题了吗? - TacB0sS
@TacB0sS:我怎么知道他们知不知道呢?:D 我记不起来有这样的问题,所以也许他们知道但是没有人抱怨,所以他们不在意。 - maaartinus
5个回答

7

Gson并不是很智能。最好提供一个清晰的、静态的数据结构,采用Java bean类的形式,这样Gson就可以理解每个属性应该被反序列化成什么类型。

例如:

public class Data {
    private Integer a;
    private String b;
    private Integer c;
    // ...
}

与之结合
Data data1 = new Data(1, "a", 2);
String json = gson.toJson(data1);
Data data2 = gson.fromJson(json, Data.class);

更新: 根据评论,键集似乎不是固定的(尽管您似乎可以在事先不知道结构的情况下手动转换它)。您可以创建一个自定义反序列化器。这里是一个快速而简单的示例。

public class ObjectDeserializer implements JsonDeserializer<Object> {

    @Override
    public Object deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException {
        String value = element.getAsString();
        try {
            return Long.valueOf(value);
        } catch (NumberFormatException e) {
            return value;
        }
    }

}

您可以按照以下方式使用它:
final Gson gson = new GsonBuilder().registerTypeAdapter(Object.class, new ObjectDeserializer()).create();
// ... 

一个Javabean肯定不可能,因为键集不是固定的。作为一种解决方法,我可以使用Map<String, String>并手动转换类型(类似于这样),我只是想知道为什么GSon的行为如此奇怪。 - maaartinus
能够手动转换类型表明您事先了解数据结构。因此,关于键集不固定的论点是无效的。至于Gson,您可以考虑使用自定义反序列化器来确定值是否为数字(正则表达式、解析器、Long#valueOf()等),并返回LongString。另请参见http://sites.google.com/site/gson/gson-user-guide#TOC-Writing-a-Deserializer。 - BalusC
我更新了答案,并且提供了一个示例,您可能会发现它有用。 - BalusC
1
因此,关于keyset不固定的争论是无效的。这个“无效的争论”使得Javabean解决方案失效了。但是感谢提供ObjectDeserializer示例,它很好用。 - maaartinus
因此,认为JavaBean不可能存在是一个无效的论点 ;) - BalusC
1
假设一个Map是一个JavaBean,没错。 ;) - maaartinus

4

升级到Gson 2.1版本。它会打印出以下内容:

a : 1.0
b : a
c : 2.0

4
Gson gson = new GsonBuilder()
    .registerTypeAdapter(Object.class, new JsonDeserializer<Object>() {
      @Override
      public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonPrimitive value = json.getAsJsonPrimitive();
        if (value.isBoolean()) {
          return value.getAsBoolean();
        } else if (value.isNumber()) {
          return value.getAsNumber();
        } else {
          return value.getAsString();
        }
      }
    }).create();

-1
如果您想从 Map<Object, Object> 获取一个 JSON 字符串,我认为 json-simpleGson 更好。
这是来自 http://code.google.com/p/json-simple/wiki/EncodingExamples 的简短示例:
//import java.util.LinkedHashMap;
//import java.util.Map;
//import org.json.simple.JSONValue;

Map obj=new LinkedHashMap();
obj.put("name","foo");
obj.put("num",new Integer(100));
obj.put("balance",new Double(1000.21));
obj.put("is_vip",new Boolean(true));
obj.put("nickname",null);
String jsonText = JSONValue.toJSONString(obj);
System.out.print(jsonText);

结果:{"name":"foo","num":100,"balance":1000.21,"is_vip":true,"nickname":null}

有关解码,请参阅http://code.google.com/p/json-simple/wiki/DecodingExamples


-1
你正在将数据存储在一个Map中。看起来你需要将对象转换为你所需的类型。

当然不会,如果类型是StringInteger,它就不能输出java.lang.Object@...。我还使用getClass()进行了双重检查。 - maaartinus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接