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

Minecraft Forge开发中gson.toJson序列化对象报模块访问异常

根本原因

异常由两个核心因素共同触发:

  • 高版本Minecraft Forge(1.17+)运行在Java 17及以上版本,Java 9引入的模块系统(JPMS)默认禁止外部模块通过反射访问JDK核心包(比如java.util)下类的私有字段,而Gson默认的序列化策略是通过反射递归遍历对象的所有字段(包括私有、继承的内部字段)完成序列化。
  • MobEffectInstance是Minecraft原生类,其内部继承链、关联字段中包含JDKjava.util包下的类实例(异常中明确提到的java.util.Random就是其中之一)。当Gson尝试反射读取这些JDK类的私有字段时,就会触发模块访问权限报错。
  • 测试用的ArrayList<Integer>、拆解后的HashMap<String, Integer>不会触发异常,是因为Integer、String等基础/包装类型有Gson内置的专属序列化器,不需要走反射遍历内部字段的逻辑,自然不会触碰JDK模块的访问限制。
规范解决方案

按推荐优先级从高到低排列:

方案1:自定义独立数据模型(最推荐)

不要直接持有Minecraft原生类的引用,专门定义只包含业务所需字段的普通类存储药水效果数据,从根源上避免Gson递归反射Minecraft、JDK内部类字段。
示例代码:

// 自定义药水效果数据类,仅保留需要的公开基础类型字段
public class PotionEffectData {
    public String effectId;
    public int duration;
    public int amplifier;
    public boolean isAmbient;

    public PotionEffectData(MobEffectInstance instance) {
        this.effectId = instance.getEffect().getDescriptionId();
        this.duration = instance.getDuration();
        this.amplifier = instance.getAmplifier();
        this.isAmbient = instance.isAmbient();
    }
}

// 修改MyInfo类的对应字段
public class MyInfo {
    public int ID;
    public float Health;
    public double X;
    public double Y;
    public double Z;
    public ArrayList<PotionEffectData> ActivePotionEffects;

    public MyInfo(Player play) {
        ID = play.getId();
        Health = play.getHealth();
        X = play.getX();
        Y = play.getY();
        Z = play.getZ();
        ActivePotionEffects = new ArrayList<>();
        for (MobEffectInstance effect : play.getActiveEffects()) {
            ActivePotionEffects.add(new PotionEffectData(effect));
        }
    }
}

这种方案结构清晰、序列化逻辑可控,没有兼容性问题,是生产环境的首选实现。

方案2:注册Gson自定义类型适配器

如果不想新增额外的数据类,可以给Gson注册MobEffectInstance的专属序列化器,明确告诉Gson序列化该类时不需要走反射,直接按指定逻辑提取字段生成JSON,完全绕开反射遍历内部字段的逻辑。
示例代码:

// 定义MobEffectInstance的序列化器
public class MobEffectInstanceSerializer implements JsonSerializer<MobEffectInstance> {
    @Override
    public JsonElement serialize(MobEffectInstance src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject effectObj = new JsonObject();
        effectObj.addProperty("effectId", src.getEffect().getDescriptionId());
        effectObj.addProperty("duration", src.getDuration());
        effectObj.addProperty("amplifier", src.getAmplifier());
        effectObj.addProperty("isAmbient", src.isAmbient());
        // 可按需增删需要序列化的字段
        return effectObj;
    }
}

// 构建Gson实例时注册适配器
Gson _Json = new GsonBuilder()
        .setPrettyPrinting()
        .registerTypeAdapter(MobEffectInstance.class, new MobEffectInstanceSerializer())
        .create();

该方案不需要修改原有MyInfo类的字段定义,仅通过Gson配置解决问题,也是Gson官方推荐的第三方类序列化处理方式。

方案3:添加JVM启动参数(不推荐)

可以通过添加JVM启动参数强制开放JDK包的反射权限绕过模块检查,参数如下:

--add-opens java.base/java.util=com.google.gson

该方案治标不治本,如果后续序列化的类中引用了其他JDK包的私有字段,还需要追加更多--add-opens配置,且模组分发后需要用户手动修改启动参数,兼容性极差,仅适合本地调试使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 10:39:28