如何用GSON序列化HashMap<Object,Object>?

How to serialize HashMap<Object,Object> with GSON?

我正在尝试序列化 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<Player,Player> matchMap=new HashMap<>();
matchMap.put(player1,opponentForPlayer1);

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

您可以使用 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 call checks if the type adapter implements more than one of these interfaces and register it for all of them.

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
    }
  }
}

根据 fluffys 的评论,最简单的解决方案是在像这样创建 gson 对象时使用 enableComplexMapKeySerialization():

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

如果那不可用或出于某种原因无法工作也可以。