提问人:Runsva 提问时间:10/26/2023 更新时间:10/28/2023 访问量:81
从 YAML 文件加载 JavaFX 形状的 LinkedList?
Load LinkedList of JavaFX Shapes from YAML file?
问:
我正在尝试使用 SnakeYAML Java 库从存储在 YAML 文件中的一系列 JavaFX 对象创建 Java。LinkedList
Shape
我编写了以下简单的 Java 类,用于将对象保存和加载到 YAML 文件中:LinkedList
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);
}
}
其中 是一个函数,该函数返回它从用户使用 选择的任何文件加载的对象,并且是将提供的对象保存到用户选择的 YAML 文件的函数。YAML_ListLoad
LinkedList
FileChooser
YAML_ListSave
LinkedList
上面的 YAML 类旨在从以下测试函数运行:
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);
}
其中只需创建 2 个 JavaFX 对象,并尝试将它们保存在 YAML 文件中,然后尝试将 YAML 文件加载到 .Save
Circle
Load
LinkedList
在上面的代码中,保存函数似乎运行良好。运行上面运行器摘录中的函数会导致创建以下 YAML 文件:Save
- !!javafx.scene.shape.Circle
accessibleHelp: null
accessibleRole: NODE
accessibleRoleDescription: null
accessibleText: null
blendMode: null
cache: false
cacheHint: DEFAULT
centerX: 0.0
centerY: 0.0
clip: null
cursor: null
depthTest: INHERIT
disable: false
effect: null
eventDispatcher: !!com.sun.javafx.scene.NodeEventDispatcher {}
fill: !!javafx.scene.paint.Color {}
focusTraversable: false
id: null
inputMethodRequests: null
layoutX: 0.0
layoutY: 0.0
managed: true
mouseTransparent: false
nodeOrientation: INHERIT
onContextMenuRequested: null
onDragDetected: null
onDragDone: null
onDragDropped: null
onDragEntered: null
onDragExited: null
onDragOver: null
onInputMethodTextChanged: null
onKeyPressed: null
onKeyReleased: null
onKeyTyped: null
onMouseClicked: null
onMouseDragEntered: null
onMouseDragExited: null
onMouseDragOver: null
onMouseDragReleased: null
onMouseDragged: null
onMouseEntered: null
onMouseExited: null
onMouseMoved: null
onMousePressed: null
onMouseReleased: null
onRotate: null
onRotationFinished: null
onRotationStarted: null
onScroll: null
onScrollFinished: null
onScrollStarted: null
onSwipeDown: null
onSwipeLeft: null
onSwipeRight: null
onSwipeUp: null
onTouchMoved: null
onTouchPressed: null
onTouchReleased: null
onTouchStationary: null
onZoom: null
onZoomFinished: null
onZoomStarted: null
opacity: 1.0
pickOnBounds: false
radius: 5.0
rotate: 0.0
rotationAxis: &id001 {}
scaleX: 1.0
scaleY: 1.0
scaleZ: 1.0
smooth: true
stroke: null
strokeDashOffset: 0.0
strokeLineCap: SQUARE
strokeLineJoin: MITER
strokeMiterLimit: 10.0
strokeType: CENTERED
strokeWidth: 1.0
style: ''
translateX: 0.0
translateY: 0.0
translateZ: 0.0
userData: null
viewOrder: 0.0
visible: true
- !!javafx.scene.shape.Circle
accessibleHelp: null
accessibleRole: NODE
accessibleRoleDescription: null
accessibleText: null
blendMode: null
cache: false
cacheHint: DEFAULT
centerX: 0.0
centerY: 0.0
clip: null
cursor: null
depthTest: INHERIT
disable: false
effect: null
eventDispatcher: !!com.sun.javafx.scene.NodeEventDispatcher {}
fill: !!javafx.scene.paint.Color {}
focusTraversable: false
id: null
inputMethodRequests: null
layoutX: 0.0
layoutY: 0.0
managed: true
mouseTransparent: false
nodeOrientation: INHERIT
onContextMenuRequested: null
onDragDetected: null
onDragDone: null
onDragDropped: null
onDragEntered: null
onDragExited: null
onDragOver: null
onInputMethodTextChanged: null
onKeyPressed: null
onKeyReleased: null
onKeyTyped: null
onMouseClicked: null
onMouseDragEntered: null
onMouseDragExited: null
onMouseDragOver: null
onMouseDragReleased: null
onMouseDragged: null
onMouseEntered: null
onMouseExited: null
onMouseMoved: null
onMousePressed: null
onMouseReleased: null
onRotate: null
onRotationFinished: null
onRotationStarted: null
onScroll: null
onScrollFinished: null
onScrollStarted: null
onSwipeDown: null
onSwipeLeft: null
onSwipeRight: null
onSwipeUp: null
onTouchMoved: null
onTouchPressed: null
onTouchReleased: null
onTouchStationary: null
onZoom: null
onZoomFinished: null
onZoomStarted: null
opacity: 1.0
pickOnBounds: false
radius: 10.0
rotate: 0.0
rotationAxis: *id001
scaleX: 1.0
scaleY: 1.0
scaleZ: 1.0
smooth: true
stroke: null
strokeDashOffset: 0.0
strokeLineCap: SQUARE
strokeLineJoin: MITER
strokeMiterLimit: 10.0
strokeType: CENTERED
strokeWidth: 1.0
style: ''
translateX: 0.0
translateY: 0.0
translateZ: 0.0
userData: null
viewOrder: 0.0
visible: true
但是,尝试通过调用将创建的 YAML 文件加载回 会导致许多错误,包括:LinkedList
Save
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 ...
^
in 'reader', line 16, column 20:
eventDispatcher: !!com.sun.javafx.scene.NodeEvent ...
^
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 ...
^
在我看来,YAML 文件中标记的条目被视为某种或空条目。YAML 解析器似乎无法解析这些内容。!!
null
从 YAML 文件加载一系列 JavaFX 对象的正确方法是什么?甚至可以使用 SnakeYAML 加载 JavaFX 对象吗?Shape
感谢您阅读我的帖子,任何指导都值得赞赏。
答:
如文档中所述,
如果自定义 Java 类符合规范,则可以加载和转储它,而无需任何额外的代码。
JavaBean
该类不符合规范,因为它不提供零参数构造函数。这就是错误消息试图告诉您的内容。com.sun.javafx.scene.NodeEventDispatcher
JavaBean
您可以编写一个自定义构造函数来解决此限制,但正如其他人所说,直接存储 UI 类不是一个好方法——通常,自动序列化对于被动数据类是可行的,但通常不适用于任何 Java 类。
关于,这些只是应用于以下节点的标签。 应用于包含所有后续键值对的以下映射,而应用于以下空映射。映射是空的,因为该类没有任何公共属性(公共值或具有 getter 和 setter 的值)。!!
!!javafx.scene.shape.Circle
!!com.sun.javafx.scene.NodeEventDispatcher
{}
标签帮助 SnakeYAML 确定它应该将一些映射加载到哪个类。当实际对象的类不等于属性的已定义静态类时,它们是必需的。例如,定义为 ,但序列化对象中的实际值是 .因此,SnakeYAML 添加了标记,以便在加载时知道它需要构造此派生类。 (然后它不起作用,因为该类不是 。eventDispatcher
javafx.event.EventDispatcher
com.sun.javafx.scene.NodeEventDispatcher
JavaBean
基于 YAML 文件格式序列化和反序列化形状的示例。
它使用杰克逊来协助编组操作。Jackson(目前)使用 SnakeYAML 作为其 YAML 实现,尽管这对通过 Jackson API 的使用是完全透明的。
它只允许单一的形状类型(a),但可以毫不费力地扩展到处理其他形状。Circle
应用输出
Wrote: /var/folders/87/r96kpgbj5tjdpsgml9gb_g8c0000gn/T/shapes-4277733445362494936.yaml
---
- !<Circle>
centerX: 40.0
centerY: 50.0
radius: 20.0
fill: "BLUE"
- !<Circle>
centerX: 20.0
centerY: 20.0
radius: 10.0
fill: "RED"
Read: /var/folders/87/r96kpgbj5tjdpsgml9gb_g8c0000gn/T/shapes-4277733445362494936.yaml
[CircleModel[centerX=40.0, centerY=50.0, radius=20.0, fill=BLUE], CircleModel[centerX=20.0, centerY=20.0, radius=10.0, fill=RED]]
使用 FXML 的替代实现
您可以将对象存储为 FXML,然后可以使用 FXMLLoader
加载它们。
如果您有一些工具来创建形状并以 FXML 格式导出它们,这可能会很好用。
但是,如果需要从应用程序中的模型对象以编程方式创建 FXML 文件,这可能有点困难。我知道没有容易获得的第三方库可以做到这一点。Jackson 与这里用于 YAML 的类似,可以通过一些额外的编程创建基本的 FXML 文件。但是,这超出了本答案的范围。
使用备用数据格式(JSON 或 XML)
若要将 JSON 用作序列化格式,请在类中使用:ShapeRepository
private final JsonFactory serializationFactory = new JsonFactory();
而不是 :YamlFactory
private final JsonFactory serializationFactory = new YAMLFactory();
同样,对于 XML,请参阅:
应用代码
模块信息.java
module com.example.persistentcirclesapp {
requires javafx.graphics;
requires com.fasterxml.jackson.dataformat.yaml;
requires com.fasterxml.jackson.databind;
exports com.example.persistentcirclesapp;
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>PersistentCirclesApp</artifactId>
<version>1.0-SNAPSHOT</version>
<name>PersistentCirclesApp</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>21.0.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.15.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.15.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
形状应用程序.java
package com.example.persistentcirclesapp;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.List;
public class ShapeApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
ShapeRepository shapeRepository = new ShapeRepository();
shapeRepository.save(
List.of(
new CircleModel(40, 50, 20, "BLUE"),
new CircleModel(20, 20, 10, "RED")
)
);
List<ShapeModel> shapes = shapeRepository.load();
ShapeRenderer shapeRenderer = new ShapeRenderer();
stage.setScene(new Scene(shapeRenderer.render(shapes)));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
形状模型.java
package com.example.persistentcirclesapp;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.WRAPPER_OBJECT
)
@JsonSubTypes({
@JsonSubTypes.Type(value = CircleModel.class, name = "Circle")
})
public sealed interface ShapeModel permits CircleModel {}
CircleModel.java
package com.example.persistentcirclesapp;
public record CircleModel(
double centerX,
double centerY,
double radius,
String fill
) implements ShapeModel {}
形状存储库.java
package com.example.persistentcirclesapp;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class ShapeRepository {
private final Path shapeRepositoryPath;
private final JsonFactory serializationFactory = new YAMLFactory();
private final ObjectMapper mapper =
new ObjectMapper(serializationFactory);
private final CollectionType shapeCollectionType = mapper.getTypeFactory().constructCollectionType(
List.class, ShapeModel.class
);
private final ObjectWriter writer = new ObjectMapper(serializationFactory)
.writerFor(shapeCollectionType);
private final ObjectReader reader = new ObjectMapper(serializationFactory)
.readerFor(shapeCollectionType);
public ShapeRepository() throws IOException {
shapeRepositoryPath = Files.createTempFile("shapes-", ".yaml");
}
public ShapeRepository(Path shapeRepositoryPath) {
this.shapeRepositoryPath = shapeRepositoryPath;
}
public void save(List<ShapeModel> shapes) throws IOException {
writer.writeValue(
shapeRepositoryPath.toFile(),
shapes
);
System.out.println("Wrote: " + shapeRepositoryPath + "\n" + Files.readString(shapeRepositoryPath));
}
public List<ShapeModel> load() throws IOException {
List<ShapeModel> shapes = reader.readValue(
shapeRepositoryPath.toFile()
);
System.out.println("Read: " + shapeRepositoryPath + "\n" + shapes);
return shapes;
}
}
ShapeRenderer.java
package com.example.persistentcirclesapp;
import javafx.scene.layout.Pane;
import java.util.List;
public class ShapeRenderer {
private final ShapeFactory shapeFactory = new ShapeFactory();
public Pane render(List<ShapeModel> shapes) {
Pane pane = new Pane();
shapes.stream()
.map(shapeFactory::createShape)
.forEach(shape ->
pane.getChildren().add(shape)
);
return pane;
}
}
ShapeFactory.java
package com.example.persistentcirclesapp;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
public class ShapeFactory {
public Shape createShape(ShapeModel shapeModel) {
return switch(shapeModel) {
case CircleModel circleModel ->
new Circle(
circleModel.centerX(),
circleModel.centerY(),
circleModel.radius(),
Paint.valueOf(circleModel.fill())
);
};
}
public ShapeModel createModel(Shape shape) {
return switch(shape) {
case Circle circle ->
new CircleModel(
circle.getCenterX(),
circle.getCenterY(),
circle.getRadius(),
circle.getFill().toString()
);
default ->
throw new IllegalArgumentException("Unsupported shape type " + shape.getClass().getName());
};
}
}
评论
com.sun.javafx.scene.NodeEvent
!!
!!
NodeEventDispatcher