Gson:有没有更简单的方法来序列化地图

Posted

技术标签:

【中文标题】Gson:有没有更简单的方法来序列化地图【英文标题】:Gson: Is there an easier way to serialize a map 【发布时间】:2012-01-11 17:57:51 【问题描述】:

This Gson 项目的链接似乎表明我必须执行以下操作才能将类型化的 Map 序列化为 JSON:

    public static class NumberTypeAdapter 
      implements JsonSerializer<Number>, JsonDeserializer<Number>,
InstanceCreator<Number> 

    public JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext
context) 
      return new JsonPrimitive(src);
    

    public Number deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
        throws JsonParseException 
      JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
      if (jsonPrimitive.isNumber()) 
        return jsonPrimitive.getAsNumber();
       else 
        throw new IllegalStateException("Expected a number field, but was " + json);
      
    

    public Number createInstance(Type type) 
      return 1L;
    
  

  public static void main(String[] args) 
    Map<String, Number> map = new HashMap<String, Number>();    
    map.put("int", 123);
    map.put("long", 1234567890123456789L);
    map.put("double", 1234.5678D);
    map.put("float", 1.2345F);
    Type mapType = new TypeToken<Map<String, Number>>() .getType();

    Gson gson = new GsonBuilder().registerTypeAdapter(Number.class, new
NumberTypeAdapter()).create();
    String json = gson.toJson(map, mapType);
    System.out.println(json);

    Map<String, Number> deserializedMap = gson.fromJson(json, mapType);
    System.out.println(deserializedMap);
  

很酷,这很有效,但似乎开销太大(整个类型适配器类?)。我使用过其他 JSON 库,例如 JSONLib,它们让您可以通过以下方式构建地图:

JSONObject json = new JSONObject();
for(Entry<String,Integer> entry : map.entrySet())
     json.put(entry.getKey(), entry.getValue());

或者,如果我有一个类似以下的自定义类:

JSONObject json = new JSONObject();
for(Entry<String,MyClass> entry : map.entrySet())
 JSONObject myClassJson =  JSONObject.fromObject(entry.getValue());
     json.put(entry.getKey(), myClassJson);

该过程更加手动,但需要的代码更少,并且没有为 Number 或在大多数情况下为我自己的自定义类创建自定义类型适配器的开销。

这是用 Gson 序列化地图的唯一方法吗,或者有没有人找到一种优于 Gson 在上面链接中推荐的方法。

【问题讨论】:

您使用的是哪个版本的 Gson?示例的输出应该是什么(不管类型适配器是否由用户提供)? 【参考方案1】:

只有TypeToken 部分是必需的(当涉及泛型时)。

Map<String, String> myMap = new HashMap<String, String>();
myMap.put("one", "hello");
myMap.put("two", "world");

Gson gson = new GsonBuilder().create();
String json = gson.toJson(myMap);

System.out.println(json);

Type typeOfHashMap = new TypeToken<Map<String, String>>()  .getType();
Map<String, String> newMap = gson.fromJson(json, typeOfHashMap); // This type must match TypeToken
System.out.println(newMap.get("one"));
System.out.println(newMap.get("two"));

输出:

"two":"world","one":"hello"
hello
world

【讨论】:

大声笑,我有时在回答问题时不注意自己的代码。是的,默认情况下 Maps/Lists/etc 是可序列化的,但您需要使用上面的 Type = new TypeToken&lt;T&gt;.getType() 语句。您仍然必须为非原始类型(或原始包装类)创建 TypeAdapter。感谢您提供正确答案,arturo。 @zygimantus TypeToken 类是public,而它的默认构造函数是protected,为了实例化对象,您可以将其扩展为匿名内部类:new TypeToken&lt;...&gt;() val typeOfHashMap = object:TypeToken>() .type //在 Kotlin 中 这个使用的底层 Map 实现是什么? (HashMap 等?) 实现是com.google.gson.internal.LinkedTreeMap。【参考方案2】:

默认

Map序列化的默认Gson实现在key上使用toString()

Gson gson = new GsonBuilder()
        .setPrettyPrinting().create();
Map<Point, String> original = new HashMap<>();
original.put(new Point(1, 2), "a");
original.put(new Point(3, 4), "b");
System.out.println(gson.toJson(original));

将给予:


  "java.awt.Point[x\u003d1,y\u003d2]": "a",
  "java.awt.Point[x\u003d3,y\u003d4]": "b"


使用enableComplexMapKeySerialization

如果您希望根据默认的 Gson 规则对 Map Key 进行序列化,您可以使用enableComplexMapKeySerialization。这将返回一个键值对数组的数组:

Gson gson = new GsonBuilder().enableComplexMapKeySerialization()
        .setPrettyPrinting().create();
Map<Point, String> original = new HashMap<>();
original.put(new Point(1, 2), "a");
original.put(new Point(3, 4), "b");
System.out.println(gson.toJson(original));

将返回:

[
  [
    
      "x": 1,
      "y": 2
    ,
    "a"
  ],
  [
    
      "x": 3,
      "y": 4
    ,
    "b"
  ]
]

更多详情请见here。

【讨论】:

为什么复杂的映射键序列化不是默认行为? @AlexSee 我赞成你的评论,但我想顾名思义,因为它只影响键,这可能是事后才添加的,因为它只有在你离开时才需要POJO 到 json,反之亦然。【参考方案3】:

在 Gson 2.7.2 中就像

一样简单
Gson gson = new Gson();
String serialized = gson.toJson(map);

【讨论】:

这仅适用于原始数据类型。对于示例中的复杂类型,您必须使用 matt burns 的答案。【参考方案4】:

我很确定 GSON 默认序列化/反序列化 Map 和多重嵌套 Map(即Map&lt;String, Map&lt;String, Object&gt;&gt;)就可以了。如果您需要做一些更复杂的事情,我认为提供的示例只是一个起点。

查看 GSON 源中的 MapTypeAdapterFactory 类:http://code.google.com/p/google-gson/source/browse/trunk/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java

只要键和值的类型可以序列化为 JSON 字符串(并且您可以为这些自定义对象创建自己的序列化器/反序列化器),您应该没有任何问题。

【讨论】:

【参考方案5】:
Map<String, Object> config = gson.fromJson(reader, Map.class);

【讨论】:

这是反序列化的方法,而不是我的问题所述的序列化方法。

以上是关于Gson:有没有更简单的方法来序列化地图的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 gson 将 json 反序列化为嵌套的自定义地图?

Gson的基本使用

Java 中 Gson的使用

Java GSON - 将int序列化为json文件的字符串

转换地图 到JSON并返回Map

android 怎么添加gson