如何用SnakeYAML从YAML文件加载JavaFX Shape的LinkedList?
问题:使用SnakeYAML序列化/反序列化JavaFX Shape对象失败
我尝试用SnakeYAML库将JavaFX Shape对象的LinkedList保存到YAML文件,再加载回来。保存功能可以生成YAML文件,但加载时抛出大量错误,核心问题是无法实例化com.sun.javafx.scene.NodeEventDispatcher这类内部类,同时Color等对象的关键信息丢失。
相关代码
YAML工具类
package com.example.test_1; import javafx.stage.FileChooser; import org.yaml.snakeyaml.Yaml; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.LinkedList; public class YAML { static public LinkedList<Object> YAML_ListLoad() { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Load"); fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("YAML File", "*.yaml")); File file = fileChooser.showOpenDialog(App.MainStage); if (file == null) { System.out.println("Unable to load YAML file because the entered directory is invalid!"); } else { try { return YAML_ListRead(file.getAbsolutePath()); } catch (IOException e) { System.out.println("Encountered error while loading YAML file!"); } } return null; } static public LinkedList<Object> YAML_ListRead(String filePath) throws IOException { Yaml yaml = new Yaml(); FileReader fileReader = new FileReader(filePath); return yaml.load(fileReader); } static public boolean YAML_ListSave(LinkedList<Object> list) { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Save"); fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("YAML File", "*.yaml")); fileChooser.setInitialFileName("file.yaml"); File file = fileChooser.showSaveDialog(App.MainStage); if (file == null) { System.out.println("Unable to save YAML file because the entered directory is invalid!"); } else { try { YAML_ListWrite(file.getAbsolutePath(), list); return true; } catch (IOException e) { System.out.println("Encountered error while saving YAML file!"); } } return false; } static public void YAML_ListWrite(String filePath, LinkedList<Object> list) throws IOException { Yaml yaml = new Yaml(); FileWriter fileWriter = new FileWriter(filePath); yaml.dump(list, fileWriter); } }
测试方法
public void Save() { LinkedList<Object> shapes = new LinkedList<Object>(); shapes.add(new Circle(5, Color.RED)); shapes.add(new Circle(10, Color.BLUE)); YAML.YAML_ListSave(shapes); } public void Load() { LinkedList<Object> shapes = YAML.YAML_ListLoad(); System.out.println(shapes); }
生成的YAML文件片段
- !!javafx.scene.shape.Circle accessibleHelp: null accessibleRole: NODE # ... 省略大量属性 eventDispatcher: !!com.sun.javafx.scene.NodeEventDispatcher {} fill: !!javafx.scene.paint.Color {} radius: 5.0 # ... 省略大量属性
加载时的错误日志
Caused by: Cannot create property=eventDispatcher for JavaBean=Circle[centerX=0.0, centerY=0.0, radius=0.0, fill=0x000000ff] in 'reader', line 1, column 3: - !!javafx.scene.shape.Circle ^ Can't construct a java object for tag:yaml.org,2002:com.sun.javafx.scene.NodeEventDispatcher; exception=java.lang.InstantiationException: NoSuchMethodException:com.sun.javafx.scene.NodeEventDispatcher.<init>() in 'reader', line 16, column 20: eventDispatcher: !!com.sun.javafx.scene.NodeEvent ... ^
解决方案
1. 优先使用数据模型(推荐)
JavaFX UI对象包含大量内部私有状态(比如eventDispatcher),这些类没有公共无参构造,不适合直接序列化。正确的做法是序列化Shape的关键数据,而非UI对象本身:
步骤1:创建数据模型POJO
import javafx.scene.paint.Color; import javafx.scene.shape.Circle; public class CircleData { private double radius; private int red; private int green; private int blue; private double opacity; // SnakeYAML要求的无参构造 public CircleData() {} // 从Circle对象提取数据 public CircleData(Circle circle) { this.radius = circle.getRadius(); Color fill = (Color) circle.getFill(); this.red = (int) (fill.getRed() * 255); this.green = (int) (fill.getGreen() * 255); this.blue = (int) (fill.getBlue() * 255); this.opacity = fill.getOpacity(); } // 将数据转换回Circle对象 public Circle toCircle() { Color color = Color.rgb(red, green, blue, opacity); return new Circle(radius, color); } // Getter和Setter方法(SnakeYAML需要访问属性) public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } public int getRed() { return red; } public void setRed(int red) { this.red = red; } public int getGreen() { return green; } public void setGreen(int green) { this.green = green; } public int getBlue() { return blue; } public void setBlue(int blue) { this.blue = blue; } public double getOpacity() { return opacity; } public void setOpacity(double opacity) { this.opacity = opacity; } }
步骤2:修改序列化/反序列化逻辑
// 修改Save方法 public void Save() { LinkedList<CircleData> shapeDataList = new LinkedList<>(); shapeDataList.add(new CircleData(new Circle(5, Color.RED))); shapeDataList.add(new CircleData(new Circle(10, Color.BLUE))); YAML.YAML_ListSave(shapeDataList); } // 修改Load方法 public void Load() { LinkedList<CircleData> shapeDataList = YAML.YAML_ListLoad(); LinkedList<Object> shapes = new LinkedList<>(); for (CircleData data : shapeDataList) { shapes.add(data.toCircle()); } System.out.println(shapes); }
2. 自定义SnakeYAML规则(仅特殊场景使用)
如果必须直接序列化JavaFX对象,可以通过自定义Constructor忽略无法实例化的属性,但这种方法局限性大,容易丢失关键信息:
import org.yaml.snakeyaml.TypeDescription; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.nodes.Node; static public LinkedList<Object> YAML_ListRead(String filePath) throws IOException { Constructor constructor = new Constructor(LinkedList.class) { @Override protected Object newInstance(Node node) { // 跳过无法实例化的内部类 String tag = node.getTag().getValue(); if (tag.equals("tag:yaml.org,2002:com.sun.javafx.scene.NodeEventDispatcher")) { return null; } return super.newInstance(node); } }; // 为Circle类忽略不需要的属性 TypeDescription circleDesc = new TypeDescription(Circle.class); circleDesc.setExcludes(new String[]{"eventDispatcher", "inputMethodRequests", "eventHandler"}); constructor.addTypeDescription(circleDesc); Yaml yaml = new Yaml(constructor); FileReader fileReader = new FileReader(filePath); return yaml.load(fileReader); }
注意:这种方法无法解决Color对象的信息丢失问题(YAML中仅保存空对象),所以依然推荐使用数据模型方案。
内容的提问来源于stack exchange,提问作者Runsva
相关产品推荐
相关产品推荐

