【问题标题】:How to serialize HashMap<Object,Object> with GSON?如何用 GSON 序列化 HashMap<Object,Object>?
【发布时间】:2021-06-16 23:12:46
【问题描述】:

我正在尝试序列化具有自定义对象(均为同一类型)作为键和值的 HashMap。但是 gson 输出的是这样的:

{ key.toString():{value}}

基本上,它不是序列化用作映射中键的对象,而是使用其 toString 值。值对象可以很好地序列化。结果 Json 显然不能反序列化。如何说服 GSON 完全序列化密钥对象?

编辑: 存储在 HashMap 中的对象包含有关玩家的信息(我正在为我们的棋盘游戏组构建自定义媒人)。它们看起来像这样:

public class Player implements Serializable{
private static final long serialVersionUID = 42L;
private String playerName,faction,teamName;
private HashMap<String,Integer> resources;

hashmap 应该包含关于即将到来的比赛的信息,基本上是这样的关键比赛:

HashMap<Player,Player> matchMap=new HashMap<>();
matchMap.put(player1,opponentForPlayer1);

【问题讨论】:

  • 您希望输出的 json 看起来如何? json 中的键是字符串。
  • 我希望有这样的东西:{ {key} : {value} } 或者,更一般地说,可以反序列化回 HashMap 的东西。
  • @BrackenAlistair BeUndead 建议您提出一个正确的问题。 {{}:{}} 不是 JSON,你必须先学习 JSON 语法。键只能是字符串。所以要么使用Map&lt;String, ...&gt;(不知道为什么需要你的密钥),要么使用javadoc.io/doc/com.google.code.gson/gson/2.8.5/com/google/gson/…,它会产生一个像[[k1,v1],[k2,v2]]这样的对元素数组JSON。无论如何,您必须检查您的方法并使用字符串键。
  • @fluffy enableComplexMapKeySerialization() 正是我想要的,你想将它作为答案提交,以便我可以将其标记为解决方案吗?

标签: java hashmap gson


【解决方案1】:

JSON 键只能是Strings。尝试以下使用自定义JsonSerializerJsonDeserializer 的解决方案。

自定义 JsonSerializer 将在序列化时将密钥(Player 对象)转换为 JSON String

import java.lang.reflect.Type;
import java.util.Map;
import java.util.Map.Entry;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

public class CustomMapSerializer implements JsonSerializer<Map<Player, Player>> {

    @Override
    public JsonElement serialize(Map<Player, Player> src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject json = new JsonObject();

        Gson gson = new Gson();

        for (Entry<Player, Player> entry : src.entrySet()) {
            json.add(gson.toJson(entry.getKey()), gson.toJsonTree(entry.getValue()));
        }

        return json;
    }

}

自定义 JsonDeserializer 以在反序列化期间将密钥 (JSON String) 转换回 Player 对象:

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import com.google.gson.Gson;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

public class CustomMapDeserializer implements JsonDeserializer<Map<Player, Player>> {

    @Override
    public Map<Player, Player> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        Map<Player, Player> players = new HashMap<Player, Player>();

        Gson gson = new Gson();

        JsonObject object = json.getAsJsonObject();

        for (Entry<String, JsonElement> entry : object.entrySet()) {
            players.put(gson.fromJson(entry.getKey(), Player.class), gson.fromJson(entry.getValue(), Player.class));
        }

        return players;
    }
}

serializationdeserialization 参考以下示例:

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Solutiony {

    public static void main(String[] args) {
        Map<String, Integer> data = new HashMap<>();
        data.put("value", 100);

        Player p = new Player();
        p.setFaction("0.9");
        p.setPlayerName("x");
        p.setTeamName("A");
        p.setResources(data);

        Player p2 = new Player();
        p2.setFaction("1.0");
        p2.setPlayerName("y");
        p2.setTeamName("B");
        p2.setResources(data);

        Map<Player, Player> map = new HashMap<Player, Player>();
        map.put(p, p2);

        Gson gson = new GsonBuilder().registerTypeAdapter(map.getClass(), new CustomMapSerializer())
                .registerTypeAdapter(map.getClass(), new CustomMapDeserializer())
                .setPrettyPrinting().create();

        //Serialization
        String val = gson.toJson(map);

        //De-serialization
        Map<Player, Player> map2 = gson.fromJson(val, map.getClass());
    }
}

序列化的JSON 看起来像:

{
  "{\"playerName\":\"x\",\"faction\":\"0.9\",\"teamName\":\"A\",\"resources\":{\"value\":100}}": {
    "playerName": "y",
    "faction": "1.0",
    "teamName": "B",
    "resources": {
      "value": 100
    }
  }
}

【讨论】:

  • 虽然这绝对是 a 解决方案,但我想问是否有人试图将其合并到我工作的代码中 ?
【解决方案2】:

基于蓬松的评论,最简单的解决方案是在创建 gson 对象时使用 enableComplexMapKeySerialization(),如下所示:

Gson gson=new GsonBuilder().enableComplexMapKeySerialization().create();

如果不可用或由于任何原因无法使用pcsutars answer 也可以使用。

【讨论】:

    【解决方案3】:

    如果没有具体的例子,很难得出准确的答案。

    您可以使用SerializedName 注解更改密钥的命名方式:

    private class SomeObject {
      @SerializedName("custom_naming") private final String someField;
      private final String someOtherField;
    
      public SomeObject(String a, String b) {
        this.someField = a;
        this.someOtherField = b;
      }
    }
    
    SomeObject someObject = new SomeObject("first", "second");
    Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
    String jsonRepresentation = gson.toJson(someObject);
    System.out.println(jsonRepresentation);
    

    输出是:

    {"custom_naming": "first", "SomeOtherField": "second"}
    

    (无耻地从文档中窃取的示例:)

    要自定义序列化,请查看文档:https://github.com/google/gson/blob/master/UserGuide.md#custom-serialization-and-deserialization

    // Step 1: writing a custom serializer
    
    
    /** Here is an example of how to write a custom serializer for JodaTime DateTime class. */
    private class DateTimeSerializer implements JsonSerializer<DateTime> {
      public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(src.toString());
      }
    }
    
    // Step 2: registering it
    GsonBuilder gson = new GsonBuilder();
    gson.registerTypeAdapter(MyType.class, new DateTimeSerializer());
    

    registerTypeAdapter 调用检查类型适配器是否实现了多个这些接口并为所有这些接口注册。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-29
      • 2016-08-20
      • 1970-01-01
      • 1970-01-01
      • 2012-12-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多