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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 23:08:12