使用Jackson序列化/反序列化BufferedImage时遇权限错误求助
我在开发游戏时尝试用JSON文件管理所有怪物数据,但使用Jackson加载数据时,出现了BufferedImage类的序列化/反序列化错误。我知道问题出在序列化操作上,但第一次用Java Jackson,不知道怎么修复。
MonsterManager代码
private static MonsterManager instance; private ArrayList<shadowStandar> monsters; private MonsterManager() { loadMonstersFromFile("res/Jsons/monsters.json"); // 根据你的文件位置调整路径 } public static MonsterManager getInstance() { if (instance == null) { instance = new MonsterManager(); } return instance; } private void loadMonstersFromFile(String filePath) { ObjectMapper objectMapper = new ObjectMapper(); try { monsters = objectMapper.readValue(new File(filePath), new TypeReference<ArrayList<shadowStandar>>() {}); } catch (IOException e) { e.printStackTrace(); // 适当处理异常 } } public ArrayList<shadowStandar> getMonsters() { return monsters; } }
shadowStandar类代码
public class shadowStandar extends Entity { BufferedImage walkDown3,walkDown4,walkUp3,walkUp4,walkLeft3,walkLeft4,walkRight3,walkRight4; public int xpGiven; private String attackType; public BufferedImage combatImage; public shadowStandar(GamePanel gp) { super(gp); } @JsonCreator public shadowStandar( @JsonProperty("gp") GamePanel gp, @JsonProperty("name") String name, @JsonProperty("health") int health, @JsonProperty("str") int str, @JsonProperty("agi") int agi, @JsonProperty("mag") int mag, @JsonProperty("vit") int vit, @JsonProperty("xpGiven") int xpGiven, @JsonProperty("attackType") String attackType, @JsonProperty("combatImagePath") String combatImagePath, @JsonProperty("weaknesses") String[] weaknesses, @JsonProperty("resistances") String[] resistances, @JsonProperty("nulls") String[] nulls, @JsonProperty("repells") String[] repells ) { super(gp); type = 2; this.name = name; this.stats.hp = health; this.stats.maxHp = this.stats.hp; this.stats.str = str; this.stats.agi = agi; this.stats.mag = mag; this.stats.vit = vit; this.xpGiven = xpGiven; this.attackType = attackType; speed = 1; loadCombatImage(combatImagePath); // 加载特定的战斗图片 getImage(); // 加载世界中的行走图片 this.weaknesses = weaknesses; this.resistances = resistances; this.nulls = nulls; this.repells = repells; } public String getAttackType(){ return attackType; } public int getPhysAttack(int playerEndurance,int attackerStat){ return 5*(int)(Math.sqrt(((double) attackerStat/playerEndurance)*randomFactor())); } public void loadCombatImage(String combatImagePath){ combatImage = setUp(combatImagePath); } }
报错信息
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 类型`java.awt.image.BufferedImage`的定义无效:无法调用`setAccess()`访问`java.awt.image.BufferedImage`类的字段'colorModel',原因是`java.lang.reflect.InaccessibleObjectException`,问题:无法使私有字段java.awt.image.ColorModel java.awt.image.BufferedImage.colorModel可访问:模块java.desktop未向未命名模块@385c9627开放`java.awt.image`包 at [Source: (File); line: 1, column: 1] at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:62) at com.fasterxml.jackson.databind.DeserializationContext.reportBadTypeDefinition(DeserializationContext.java:1893) at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder._handleBadAccess(BeanDeserializerBuilder.java:620) at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder._fixAccess(BeanDeserializerBuilder.java:537) at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder.build(BeanDeserializerBuilder.java:384) at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:295) at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:151) at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:415) at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:350) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:644) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:539) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:294) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:621) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:188) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:28) at com.fasterxml.jackson.databind.DeserializationContext.handleSecondaryContextualization(DeserializationContext.java:867) at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:659) at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4956) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4826) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3657) at monster.MonsterManager.loadMonstersFromFile(MonsterManager.java:28) at monster.MonsterManager.<init>(MonsterManager.java:15) at monster.MonsterManager.getInstance(MonsterManager.java:20) at main.GamePanel.<init>(GamePanel.java:51) at main.Main.main(Main.java:14) Caused by: java.lang.IllegalArgumentException: Failed to call `setAccess()` on Field 'colorModel' (of class `java.awt.image.BufferedImage`) due to `java.lang.reflect.InaccessibleObjectException`, problem: Unable to make field private java.awt.image.ColorModel java.awt.image.BufferedImage.colorModel accessible: module java.desktop does not "opens java.awt.image" to unnamed module @385c9627 at com.fasterxml.jackson.databind.util.ClassUtil.checkAndFixAccess(ClassUtil.java:1008) at com.fasterxml.jackson.databind.deser.impl.FieldProperty.fixAccess(FieldProperty.java:104) at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder._fixAccess(BeanDeserializerBuilder.java:535) ... 26 more Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private java.awt.image.ColorModel java.awt.image.BufferedImage.colorModel accessible: module java.desktop does not "opens java.awt.image" to unnamed module @385c9627 at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:387) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:363) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:311) at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:181) at java.base/java.lang.reflect.Field.setAccessible(Field.java:175) at com.fasterxml.jackson.databind.util.ClassUtil.checkAndFixAccess(ClassUtil.java:995) ... 28 more
问题根源
Jackson尝试自动序列化/反序列化shadowStandar类中的BufferedImage字段,但BufferedImage属于Java桌面模块(java.desktop),其内部私有字段受模块系统保护,Jackson无法通过反射访问这些字段;同时BufferedImage是二进制对象,不适合存储在纯文本的JSON文件中。
修复步骤
1. 让Jackson忽略BufferedImage字段
给所有BufferedImage字段添加@JsonIgnore注解,阻止Jackson尝试处理这些字段:
public class shadowStandar extends Entity { @JsonIgnore BufferedImage walkDown3,walkDown4,walkUp3,walkUp4,walkLeft3,walkLeft4,walkRight3,walkRight4; public int xpGiven; private String attackType; @JsonIgnore public BufferedImage combatImage; // 其余代码保持不变... }
如果Entity父类中也有BufferedImage字段,同样需要给这些字段添加@JsonIgnore。
2. 确保JSON只存储图片路径
你的JSON文件中应该只存储图片的相对/绝对路径(比如combatImagePath),而不是图片对象本身。示例JSON结构:
[ { "name": "Shadow Monster", "health": 100, "str": 20, "agi": 15, "mag": 10, "vit": 12, "xpGiven": 50, "attackType": "physical", "combatImagePath": "res/sprites/monsters/shadow_combat.png", "weaknesses": ["fire"], "resistances": ["dark"], "nulls": ["light"], "repells": [] } ]
注意:GamePanel对象无法被Jackson序列化,需要在加载怪物后手动设置——比如在MonsterManager中遍历怪物,调用setGp方法传入实例。
3. 确认图片加载逻辑正常
你的@JsonCreator构造函数已经通过combatImagePath参数调用loadCombatImage加载图片,getImage方法也会加载行走动画图片,这部分逻辑保持不变即可。
4. 可选:优化ObjectMapper配置
如果存在未知字段导致的序列化错误,可以给ObjectMapper添加忽略未知字段的配置,并手动设置GamePanel:
private void loadMonstersFromFile(String filePath) { ObjectMapper objectMapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); try { monsters = objectMapper.readValue(new File(filePath), new TypeReference<ArrayList<shadowStandar>>() {}); // 手动给每个怪物设置GamePanel GamePanel gp = new GamePanel(); // 或使用已有的GamePanel实例 for(shadowStandar monster : monsters) { monster.setGp(gp); // 假设Entity类有对应的setter方法 } } catch (IOException e) { e.printStackTrace(); } }
总结
不要在JSON中存储二进制对象(比如BufferedImage),而是存储它们的路径或其他可序列化的元数据,在对象实例化后再手动加载二进制资源,这是游戏开发中管理资源的标准做法,既避免了序列化问题,也让JSON文件更简洁易维护。
内容的提问来源于stack exchange,提问作者Alejandrol

