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

Gson调用fromJson反序列化私有字段报无法访问错误如何解决

问题根因

这个错误和Gson是否支持私有字段没有关系,本质是Java 9及以上版本引入的JPMS模块系统的反射访问限制导致的。
你贴的错误堆栈最底部的根因信息已经明确给出了触发原因:

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.String com.example.server.Models.Game.name accessible: module com.example.server does not "opens com.example.server.Models" to module com.google.gson

Gson默认通过反射调用setAccessible(true)绕过访问修饰符限制,直接读写类的私有字段完成序列化、反序列化。Java 8及之前版本没有模块系统,反射可以随意访问私有成员,所以你查到的旧教程里直接用private字段都能正常运行。
Java 9之后的模块化规则默认禁止外部模块对未开放的包做深度反射访问,Gson所在模块拿不到你com.example.server.Models包的反射权限,自然无法访问私有字段。把字段改成public能正常运行,是因为public成员属于模块正常导出的可访问范围,不需要申请反射权限。

解决方案

以下方案按推荐优先级排序:

  • 方案1(最推荐,符合模块系统规范):在模块配置文件中开放对应包的反射权限
    找到项目源码目录下的module-info.java文件,添加opens声明,把存放实体类的包开放给Gson模块:

    module com.example.server {
        // 保留你原有的requires、exports等配置
        // 新增下面这行,开放Models包给Gson做反射访问
        opens com.example.server.Models to com.google.gson;
    }
    

    如果项目中还有其他需要Gson序列化/反序列化的类所在的包,按相同格式追加opens声明即可。这个方案不需要修改原有类的字段修饰符,也不需要写额外的适配代码,是模块化项目的标准解法。

  • 方案2(适用于非模块化传统项目):添加JVM启动参数开放反射权限
    如果你没有写module-info.java,是靠传统classpath模式运行的项目,可以在程序启动的JVM参数中添加如下配置,给Gson开放对应包的访问权限:

    --add-opens com.example.server/com.example.server.Models=com.google.gson
    

    不推荐直接加全局开放权限的参数,会放大安全风险。

  • 方案3(无反射,性能更好):为实体类编写自定义TypeAdapter
    这种方案完全不依赖反射访问私有字段,手动实现JSON和实体类的转换逻辑,彻底绕开模块访问限制,示例代码如下:

    public class GameTypeAdapter extends TypeAdapter<Game> {
        @Override
        public void write(JsonWriter out, Game game) throws IOException {
            out.beginObject();
            out.name("name").value(game.getName());
            out.name("id").value(game.getId());
            out.name("gameFields").jsonValue(toJson(game.getGameFields()));
            out.endObject();
        }
    
        @Override
        public Game read(JsonReader in) throws IOException {
            Game game = new Game();
            in.beginObject();
            while (in.hasNext()) {
                String fieldName = in.nextName();
                switch (fieldName) {
                    case "name" -> game.setName(in.nextString());
                    case "id" -> game.setId(in.nextInt());
                    case "gameFields" -> game.setGameFields(fromJson(in, GameFields[].class));
                    // 不需要反序列化的players字段直接跳过即可
                    default -> in.skipValue();
                }
            }
            in.endObject();
            return game;
        }
    }
    

    写完后把适配器注册到Gson实例中即可正常使用:

    Gson gson = new GsonBuilder()
            .registerTypeAdapter(Game.class, new GameTypeAdapter())
            .create();
    

    这个方案的序列化性能比反射实现更好,缺点是每个实体类都需要写对应的转换逻辑,代码量更大。

  • 不推荐方案:把所有类的字段改成public,或者强制给所有字段加public的getter/setter供Gson访问,这种写法会破坏类的封装性,没有必要。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 09:27:10