You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Gson序列化含非基本类型键HashMap的类时遇编译错误求助

问题:Gson序列化含非基本类型键的HashMap时键显示哈希字符串且编译报错

我想用Gson把包含非基本类型键(Tile对象)的HashMap的Java类序列化为JSON,但默认情况下Gson会将HashMap的键转为哈希字符串(比如dir.Tile@6996db8)。参考官方的MapTypeAdapterFactory实现时,编译报错:

java: cannot find symbol
  symbol:   class TypeAdapterRuntimeTypeWrapper
  location: class TypeAdapters.MapTypeAdapterFactory.Adapter<K,V>

原始代码

public class App {
    public static void main(String[] args) throws Exception {

        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        GameController gameController = new GameController();
        
        System.out.println(gson.toJson(gameController));
    }
}


class GameController
{
    HashMap<Tile, TileState> map = new HashMap<>();

    public GameController()
    {
        map.put(new Tile(), TileState.VISIBLE);
        map.put(new Tile(), TileState.REVEALED);
    }
}

class Tile 
{

    @Override
    public int hashCode() {
        return super.hashCode();
    }
}

enum TileState
{
    REVEALED,
    VISIBLE
}

默认序列化输出

{
  "map": {
    "dir.Tile@6996db8": "REVEALED",
    "dir.Tile@368102c8": "VISIBLE"
  }
}

解决方案

核心原因

TypeAdapterRuntimeTypeWrapper是Gson内部私有类,不属于公开API,直接复制官方的MapTypeAdapterFactory代码会因无法引用该类导致编译错误。我们需要自定义TypeAdapter来处理非基本类型键的Map序列化。

步骤1:完善Tile类(保证HashMap正确性+可序列化)

你的Tile类目前没有业务属性,且hashCode直接继承自Object,会导致每个Tile对象都是唯一键(即使逻辑上相同)。先完善Tile类:

import java.util.Objects;

class Tile {
    // 添加业务标识属性,比如坐标
    private int x;
    private int y;

    public Tile() {}
    public Tile(int x, int y) {
        this.x = x;
        this.y = y;
    }

    // Getter & Setter
    public int getX() { return x; }
    public void setX(int x) { this.x = x; }
    public int getY() { return y; }
    public void setY(int y) { this.y = y; }

    // 重写equals和hashCode,保证HashMap键的正确性
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Tile tile = (Tile) o;
        return x == tile.x && y == tile.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

步骤2:自定义Map TypeAdapter

针对Map<Tile, TileState>编写自定义适配器,实现键的正确序列化与反序列化:

import com.google.gson.TypeAdapter;
import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class TileMapTypeAdapter extends TypeAdapter<Map<Tile, TileState>> {
    private final Gson gson = new Gson();

    @Override
    public void write(JsonWriter out, Map<Tile, TileState> map) throws IOException {
        out.beginObject();
        for (Map.Entry<Tile, TileState> entry : map.entrySet()) {
            // 将Tile序列化为JSON字符串作为键
            String keyJson = gson.toJson(entry.getKey());
            out.name(keyJson);
            gson.toJson(entry.getValue(), TileState.class, out);
        }
        out.endObject();
    }

    @Override
    public Map<Tile, TileState> read(JsonReader in) throws IOException {
        Map<Tile, TileState> map = new HashMap<>();
        in.beginObject();
        while (in.hasNext()) {
            // 将键的JSON字符串反序列化为Tile对象
            String keyJson = in.nextName();
            Tile tile = gson.fromJson(keyJson, Tile.class);
            TileState state = gson.fromJson(in, TileState.class);
            map.put(tile, state);
        }
        in.endObject();
        return map;
    }
}

步骤3:注册适配器并使用

在创建Gson实例时注册自定义适配器,同时更新GameController的构造方法:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

public class App {
    public static void main(String[] args) throws Exception {
        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                // 注册自定义适配器
                .registerTypeAdapter(new TypeToken<Map<Tile, TileState>>(){}.getType(), new TileMapTypeAdapter())
                .create();
        GameController gameController = new GameController();
        System.out.println(gson.toJson(gameController));
    }
}

class GameController {
    HashMap<Tile, TileState> map = new HashMap<>();

    public GameController() {
        // 使用带属性的Tile对象
        map.put(new Tile(0, 0), TileState.VISIBLE);
        map.put(new Tile(1, 1), TileState.REVEALED);
    }
}

enum TileState {
    REVEALED,
    VISIBLE
}

可选优化:将Map序列化为数组结构

如果不希望用JSON字符串作为键(可读性较差),可以修改TypeAdapter的write方法,将Map转为数组结构:

@Override
public void write(JsonWriter out, Map<Tile, TileState> map) throws IOException {
    out.beginArray();
    for (Map.Entry<Tile, TileState> entry : map.entrySet()) {
        out.beginObject();
        out.name("tile");
        gson.toJson(entry.getKey(), Tile.class, out);
        out.name("state");
        gson.toJson(entry.getValue(), TileState.class, out);
        out.endObject();
    }
    out.endArray();
}

对应的输出会更直观:

{
  "map": [
    {
      "tile": {
        "x": 0,
        "y": 0
      },
      "state": "VISIBLE"
    },
    {
      "tile": {
        "x": 1,
        "y": 1
      },
      "state": "REVEALED"
    }
  ]
}

内容的提问来源于stack exchange,提问作者Reza Masoumi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 01:24:13