JavaFX:如何将 ObservableList 读写到文件中?
JavaFX: How to read and write an ObservableList to a file?
我是编程新手,我想做的是读取/写入存储在[=13中的数据=],数据来自 Student class,它们是字符串(firstName 和 lastName),我还有一个 TableView 来显示数据。
这是我的代码:
学生Class:
import java.io.Serializable;
import javafx.beans.property.SimpleStringProperty;
public class Student implements Serializable {
private SimpleStringProperty fname;
private SimpleStringProperty lname;
Student() {
this("","");
}
Student(String fn, String ln) {
this.fname = new SimpleStringProperty(fn);
this.lname = new SimpleStringProperty(ln);
}
public void setFirstName(String f) {
fname.set(f);
}
public String getFirstName() {
return fname.get();
}
public void setLastName(String l) {
lname.set(l);
}
public String getLastName() {
return lname.get();
}
@Override
public String toString() {
return String.format("%s %s", getFirstName(), getLastName());
}
}
这是我使用 TextFields 输入数据的代码:
@FXML
ObservableList<Student> data = FXCollections.observableArrayList();
//Just to input the data
@FXML
private void handleButtonAction(ActionEvent event) {
if(!"".equals(txtFirstName.getText()) && !"".equals(txtLastName.getText())){
data.add(
new Student(txtFirstName.getText(),
txtLastName.getText()
));
}
txtFirstName.clear();
txtLastName.clear();
// System.out.println(data);
}
问题来了...
Reading/Writing ObservableList:
@FXML
private void HandleMenuSaveAction(ActionEvent event) {
try {
FileOutputStream f = new FileOutputStream(new File("saveStudentList.txt"));
ObjectOutputStream o = new ObjectOutputStream(f);
o.writeObject(data);
o.close();
f.close();
System.out.println("File Saved Successfully.");
} catch (FileNotFoundException ex) {
System.err.println("Save: File not found.");
} catch (IOException ex) {
System.err.println("Save: Error initializing stream.");
ex.printStackTrace();
}
}
@FXML
private void HandleMenuLoadAction(ActionEvent event) {
try {
FileInputStream fi = new FileInputStream(new File("saveStudentList.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
data = (ObservableList) oi.readObject();
System.out.println(data.toString());
//Refresh the Table everytime we load data
oi.close();
fi.close();
} catch (FileNotFoundException ex) {
System.err.println("Load: File not found.");
} catch (IOException ex) {
System.err.println("Load: Error initializing stream.");
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
这导致我 java.io.NotSerializableException
,
有谁知道如何更改我的代码以使其正常工作?
为 Student
对象实施自定义序列化(参见 )并将 ObservableList
的内容复制到 ArrayList
以创建可序列化列表:
public class Student implements Serializable {
private void writeObject(ObjectOutputStream out)
throws IOException {
out.writeObject(getFirstName());
out.writeObject(getLastName());
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
fname = new SimpleStringProperty((String) in.readObject());
lname = new SimpleStringProperty((String) in.readObject());
}
...
}
(反)序列化示例
ObservableList<Student> students = FXCollections.observableArrayList();
for(int i = 0; i < 100; i++) {
students.add(new Student("Mark"+i, "Miller"+i));
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// write list
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(new ArrayList<>(students));
}
students = null; // make sure the old reference is no longer available
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
// read list
try (ObjectInputStream ois = new ObjectInputStream(bis)){
students = FXCollections.observableList((List<Student>) ois.readObject());
}
System.out.println(students);
您的问题有点令人困惑,因为您正在使用对象序列化,但您选择的文件名是 .txt
文件。序列化对象不像纯文本文件那样是人类可读的。
如果您想使用序列化,Fabian 上面的回答很好。但是,如果您希望生成一个简单的文本文件,请查看以下示例程序:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
ObservableList<Student> students = FXCollections.observableArrayList();
// Create some sample students
students.addAll(
new Student("Roger", "Rabbit"),
new Student("Elmer", "Fudd"),
new Student("Daffy", "Duck"),
new Student("Foghorn", "Leghorn"),
new Student("Betty", "Boop")
);
// Write the list to a text file
try {
writeToTextFile("students.txt", students);
} catch (IOException e) {
e.printStackTrace();
}
// Now, read the file into a new List<Student>
List<Student> inputStudents = null;
try {
inputStudents = readStudents("students.txt");
} catch (IOException e) {
e.printStackTrace();
}
// Print out the student names
if (inputStudents != null) {
for (Student student : inputStudents) {
System.out.println(student.toString());
}
}
}
/**
* Write the list of students to a simple text file with first and last names separated by a comma
*/
private static void writeToTextFile(String filename, ObservableList<Student> students)
throws IOException {
FileWriter writer = new FileWriter(filename);
for (Student student : students) {
writer.write(student.getFirstName() + "," + student.getLastName() + "\n");
}
writer.close();
}
/**
* Read the comma-separated list of student names from the text file
*/
private static List<Student> readStudents(String filename)
throws IOException {
List<Student> students = new ArrayList<>();
BufferedReader reader = Files.newBufferedReader(Paths.get(filename));
String line;
while ((line = reader.readLine()) != null) {
String[] names = line.split(",");
// Add the student to the list
students.add(new Student(names[0], names[1]));
}
return students;
}
}
这会生成一个非常简单的文本文件,每个学生的名字和姓氏各占一行,以逗号分隔。然后将其读回到新列表中,准备好在您的 TableView
.
中使用
如果您决定走这条路,我建议您找一个好的 CSV 库来处理 reading/writing 的 CSV(逗号分隔值)文件。 Apache Commons 有一个不错的可用。
这里是一个示例,它以 json 格式从一个 ObservableList 人员(名字和姓氏)读取数据并将其写入文件。
列表中已保存 json 数据的示例文件内容
[ {
"firstName" : "Fred",
"lastName" : "Flintstone"
}, {
"firstName" : "Wilma",
"lastName" : "Flintstone"
}, {
"firstName" : "Barney",
"lastName" : "Rubble"
} ]
实施说明
数据项存储为人员记录。
ObservableList 支持 TableView 并保存数据项的记录。
第 3 方 Jackson 库用于将数据列表序列化和反序列化为 JSON,存储并从文件中读取。
启动时,应用程序会生成一个临时文件名,用于在应用程序的生命周期内存储已保存的数据文件。
关机时,临时存档会自动删除。
module-info 允许 Jackson 数据绑定模块对包含要保存的项目的记录定义的包执行反射。
在保存和恢复数据项之前,它们被临时存储在一个ArrayList中,而不是一个ObservableList中。这样做是因为您不想尝试序列化整个 ObservableList。 ObservableList 还将具有可能附加到列表的更改侦听器的条目。您不想序列化那些侦听器。
使用 Jackson 执行序列化和反序列化的 ListSerializer class 使用 Java 泛型,因此它可以保存和加载可以通过 Jackson 序列化的任何类型的数据(包括 Person 记录在例子)。泛型在代码中增加了一些复杂性,以确定要在序列化和反序列化过程中使用的正确类型。泛型确实允许更通用的解决方案,因此,总的来说,我认为添加通用解决方案值得权衡实现中的额外复杂性。
ListSerializerController 演示了如何使用 ListSerializer 将数据保存和加载到支持 TableView 的 ObservableList。
Maven 用作构建系统。
JRE 18 和 JavaFX 18 用作运行时。
示例解决方案
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>ListSerialization</artifactId>
<version>1.0-SNAPSHOT</version>
<name>ListSerialization</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.8.1</junit.version>
<javafx.version>18</javafx.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>18</source>
<target>18</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
src/main/java/module-info.java
module com.example.listserialization {
requires javafx.controls;
requires com.fasterxml.jackson.databind;
opens com.example.listserialization to com.fasterxml.jackson.databind;
exports com.example.listserialization;
}
src/main/java/com/example/listserialization/ListSerializerApp.java
package com.example.listserialization;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class ListSerializerApp extends Application {
private Path peoplePath;
@Override
public void init() throws IOException {
peoplePath = Files.createTempFile(
"people",
".json"
);
peoplePath.toFile().deleteOnExit();
System.out.println("Using save file name: " + peoplePath);
}
@Override
public void start(Stage stage) throws IOException {
ListSerializerController listSerializerController = new ListSerializerController(
peoplePath
);
stage.setScene(
new Scene(
listSerializerController.getLayout()
)
);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
src/main/java/com/example/listserialization/Person.java
package com.example.listserialization;
record Person(String firstName, String lastName) {}
src/main/java/com/example/listserialization/ListSerializer.java
package com.example.listserialization;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.CollectionType;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
public class ListSerializer<T> {
private static final ObjectMapper mapper =
new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT);
private final CollectionType listType;
public ListSerializer(Class<T> listItemClass) {
listType =
mapper.getTypeFactory()
.constructCollectionType(
ArrayList.class,
listItemClass
);
}
public void serializeList(Path path, ArrayList<T> list) throws IOException {
Files.writeString(
path,
mapper.writeValueAsString(
list
)
);
}
public ArrayList<T> deserializeList(Path path) throws IOException {
return mapper.<ArrayList<T>>readValue(
Files.readString(path),
listType
);
}
}
src/main/java/com/example/listserialization/ListSerializerController.java
package com.example.listserialization;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
public class ListSerializerController {
private final ListSerializer<Person> listSerializer = new ListSerializer<>(
Person.class
);
private final Person[] TEST_PEOPLE = {
new Person("Fred", "Flintstone"),
new Person("Wilma", "Flintstone"),
new Person("Barney", "Rubble")
};
private final TableView<Person> tableView = new TableView<>(
FXCollections.observableArrayList(
TEST_PEOPLE
)
);
private final Path peoplePath;
private final Parent layout;
public ListSerializerController(Path peoplePath) {
this.peoplePath = peoplePath;
layout = createLayout();
}
private Parent createLayout() {
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(p ->
new ReadOnlyStringWrapper(
p.getValue().firstName()
).getReadOnlyProperty()
);
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setCellValueFactory(p ->
new ReadOnlyStringWrapper(
p.getValue().lastName()
).getReadOnlyProperty()
);
//noinspection unchecked
tableView.getColumns().setAll(firstNameCol, lastNameCol);
tableView.setPrefHeight(150);
Button save = new Button("Save");
save.setOnAction(this::save);
Button clear = new Button("Clear");
clear.setOnAction(this::clear);
Button load = new Button("Load");
load.setOnAction(this::load);
Button restoreDefault = new Button("Default Data");
restoreDefault.setOnAction(this::restoreDefault);
HBox controls = new HBox(10, save, clear, load, restoreDefault);
VBox layout = new VBox(10, controls, tableView);
layout.setPadding(new Insets(10));
return layout;
}
public Parent getLayout() {
return layout;
}
private void save(ActionEvent e) {
try {
listSerializer.serializeList(
peoplePath,
new ArrayList<>(
tableView.getItems()
)
);
System.out.println("Saved to: " + peoplePath);
System.out.println(Files.readString(peoplePath));
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void clear(ActionEvent e) {
tableView.getItems().clear();
System.out.println("Cleared data in UI");
}
private void load(ActionEvent e) {
try {
if (!peoplePath.toFile().exists()) {
tableView.getItems().clear();
System.out.println("Saved data file does not exist, clearing data: " + peoplePath);
return;
}
tableView.getItems().setAll(
listSerializer.deserializeList(peoplePath)
);
System.out.println("Loaded data from: " + peoplePath);
System.out.println(Files.readString(peoplePath));
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void restoreDefault(ActionEvent e) {
tableView.getItems().setAll(TEST_PEOPLE);
System.out.println("Restored default data in UI");
}
}
我是编程新手,我想做的是读取/写入存储在[=13中的数据=],数据来自 Student class,它们是字符串(firstName 和 lastName),我还有一个 TableView 来显示数据。
这是我的代码:
学生Class:
import java.io.Serializable;
import javafx.beans.property.SimpleStringProperty;
public class Student implements Serializable {
private SimpleStringProperty fname;
private SimpleStringProperty lname;
Student() {
this("","");
}
Student(String fn, String ln) {
this.fname = new SimpleStringProperty(fn);
this.lname = new SimpleStringProperty(ln);
}
public void setFirstName(String f) {
fname.set(f);
}
public String getFirstName() {
return fname.get();
}
public void setLastName(String l) {
lname.set(l);
}
public String getLastName() {
return lname.get();
}
@Override
public String toString() {
return String.format("%s %s", getFirstName(), getLastName());
}
}
这是我使用 TextFields 输入数据的代码:
@FXML
ObservableList<Student> data = FXCollections.observableArrayList();
//Just to input the data
@FXML
private void handleButtonAction(ActionEvent event) {
if(!"".equals(txtFirstName.getText()) && !"".equals(txtLastName.getText())){
data.add(
new Student(txtFirstName.getText(),
txtLastName.getText()
));
}
txtFirstName.clear();
txtLastName.clear();
// System.out.println(data);
}
问题来了...
Reading/Writing ObservableList:
@FXML
private void HandleMenuSaveAction(ActionEvent event) {
try {
FileOutputStream f = new FileOutputStream(new File("saveStudentList.txt"));
ObjectOutputStream o = new ObjectOutputStream(f);
o.writeObject(data);
o.close();
f.close();
System.out.println("File Saved Successfully.");
} catch (FileNotFoundException ex) {
System.err.println("Save: File not found.");
} catch (IOException ex) {
System.err.println("Save: Error initializing stream.");
ex.printStackTrace();
}
}
@FXML
private void HandleMenuLoadAction(ActionEvent event) {
try {
FileInputStream fi = new FileInputStream(new File("saveStudentList.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
data = (ObservableList) oi.readObject();
System.out.println(data.toString());
//Refresh the Table everytime we load data
oi.close();
fi.close();
} catch (FileNotFoundException ex) {
System.err.println("Load: File not found.");
} catch (IOException ex) {
System.err.println("Load: Error initializing stream.");
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
这导致我 java.io.NotSerializableException
,
有谁知道如何更改我的代码以使其正常工作?
为 Student
对象实施自定义序列化(参见 )并将 ObservableList
的内容复制到 ArrayList
以创建可序列化列表:
public class Student implements Serializable {
private void writeObject(ObjectOutputStream out)
throws IOException {
out.writeObject(getFirstName());
out.writeObject(getLastName());
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
fname = new SimpleStringProperty((String) in.readObject());
lname = new SimpleStringProperty((String) in.readObject());
}
...
}
(反)序列化示例
ObservableList<Student> students = FXCollections.observableArrayList();
for(int i = 0; i < 100; i++) {
students.add(new Student("Mark"+i, "Miller"+i));
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// write list
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(new ArrayList<>(students));
}
students = null; // make sure the old reference is no longer available
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
// read list
try (ObjectInputStream ois = new ObjectInputStream(bis)){
students = FXCollections.observableList((List<Student>) ois.readObject());
}
System.out.println(students);
您的问题有点令人困惑,因为您正在使用对象序列化,但您选择的文件名是 .txt
文件。序列化对象不像纯文本文件那样是人类可读的。
如果您想使用序列化,Fabian 上面的回答很好。但是,如果您希望生成一个简单的文本文件,请查看以下示例程序:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
ObservableList<Student> students = FXCollections.observableArrayList();
// Create some sample students
students.addAll(
new Student("Roger", "Rabbit"),
new Student("Elmer", "Fudd"),
new Student("Daffy", "Duck"),
new Student("Foghorn", "Leghorn"),
new Student("Betty", "Boop")
);
// Write the list to a text file
try {
writeToTextFile("students.txt", students);
} catch (IOException e) {
e.printStackTrace();
}
// Now, read the file into a new List<Student>
List<Student> inputStudents = null;
try {
inputStudents = readStudents("students.txt");
} catch (IOException e) {
e.printStackTrace();
}
// Print out the student names
if (inputStudents != null) {
for (Student student : inputStudents) {
System.out.println(student.toString());
}
}
}
/**
* Write the list of students to a simple text file with first and last names separated by a comma
*/
private static void writeToTextFile(String filename, ObservableList<Student> students)
throws IOException {
FileWriter writer = new FileWriter(filename);
for (Student student : students) {
writer.write(student.getFirstName() + "," + student.getLastName() + "\n");
}
writer.close();
}
/**
* Read the comma-separated list of student names from the text file
*/
private static List<Student> readStudents(String filename)
throws IOException {
List<Student> students = new ArrayList<>();
BufferedReader reader = Files.newBufferedReader(Paths.get(filename));
String line;
while ((line = reader.readLine()) != null) {
String[] names = line.split(",");
// Add the student to the list
students.add(new Student(names[0], names[1]));
}
return students;
}
}
这会生成一个非常简单的文本文件,每个学生的名字和姓氏各占一行,以逗号分隔。然后将其读回到新列表中,准备好在您的 TableView
.
如果您决定走这条路,我建议您找一个好的 CSV 库来处理 reading/writing 的 CSV(逗号分隔值)文件。 Apache Commons 有一个不错的可用。
这里是一个示例,它以 json 格式从一个 ObservableList 人员(名字和姓氏)读取数据并将其写入文件。
列表中已保存 json 数据的示例文件内容
[ {
"firstName" : "Fred",
"lastName" : "Flintstone"
}, {
"firstName" : "Wilma",
"lastName" : "Flintstone"
}, {
"firstName" : "Barney",
"lastName" : "Rubble"
} ]
实施说明
数据项存储为人员记录。
ObservableList 支持 TableView 并保存数据项的记录。
第 3 方 Jackson 库用于将数据列表序列化和反序列化为 JSON,存储并从文件中读取。
启动时,应用程序会生成一个临时文件名,用于在应用程序的生命周期内存储已保存的数据文件。
关机时,临时存档会自动删除。
module-info 允许 Jackson 数据绑定模块对包含要保存的项目的记录定义的包执行反射。
在保存和恢复数据项之前,它们被临时存储在一个ArrayList中,而不是一个ObservableList中。这样做是因为您不想尝试序列化整个 ObservableList。 ObservableList 还将具有可能附加到列表的更改侦听器的条目。您不想序列化那些侦听器。
使用 Jackson 执行序列化和反序列化的 ListSerializer class 使用 Java 泛型,因此它可以保存和加载可以通过 Jackson 序列化的任何类型的数据(包括 Person 记录在例子)。泛型在代码中增加了一些复杂性,以确定要在序列化和反序列化过程中使用的正确类型。泛型确实允许更通用的解决方案,因此,总的来说,我认为添加通用解决方案值得权衡实现中的额外复杂性。
ListSerializerController 演示了如何使用 ListSerializer 将数据保存和加载到支持 TableView 的 ObservableList。
Maven 用作构建系统。
JRE 18 和 JavaFX 18 用作运行时。
示例解决方案
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>ListSerialization</artifactId>
<version>1.0-SNAPSHOT</version>
<name>ListSerialization</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.8.1</junit.version>
<javafx.version>18</javafx.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>18</source>
<target>18</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
src/main/java/module-info.java
module com.example.listserialization {
requires javafx.controls;
requires com.fasterxml.jackson.databind;
opens com.example.listserialization to com.fasterxml.jackson.databind;
exports com.example.listserialization;
}
src/main/java/com/example/listserialization/ListSerializerApp.java
package com.example.listserialization;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class ListSerializerApp extends Application {
private Path peoplePath;
@Override
public void init() throws IOException {
peoplePath = Files.createTempFile(
"people",
".json"
);
peoplePath.toFile().deleteOnExit();
System.out.println("Using save file name: " + peoplePath);
}
@Override
public void start(Stage stage) throws IOException {
ListSerializerController listSerializerController = new ListSerializerController(
peoplePath
);
stage.setScene(
new Scene(
listSerializerController.getLayout()
)
);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
src/main/java/com/example/listserialization/Person.java
package com.example.listserialization;
record Person(String firstName, String lastName) {}
src/main/java/com/example/listserialization/ListSerializer.java
package com.example.listserialization;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.CollectionType;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
public class ListSerializer<T> {
private static final ObjectMapper mapper =
new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT);
private final CollectionType listType;
public ListSerializer(Class<T> listItemClass) {
listType =
mapper.getTypeFactory()
.constructCollectionType(
ArrayList.class,
listItemClass
);
}
public void serializeList(Path path, ArrayList<T> list) throws IOException {
Files.writeString(
path,
mapper.writeValueAsString(
list
)
);
}
public ArrayList<T> deserializeList(Path path) throws IOException {
return mapper.<ArrayList<T>>readValue(
Files.readString(path),
listType
);
}
}
src/main/java/com/example/listserialization/ListSerializerController.java
package com.example.listserialization;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
public class ListSerializerController {
private final ListSerializer<Person> listSerializer = new ListSerializer<>(
Person.class
);
private final Person[] TEST_PEOPLE = {
new Person("Fred", "Flintstone"),
new Person("Wilma", "Flintstone"),
new Person("Barney", "Rubble")
};
private final TableView<Person> tableView = new TableView<>(
FXCollections.observableArrayList(
TEST_PEOPLE
)
);
private final Path peoplePath;
private final Parent layout;
public ListSerializerController(Path peoplePath) {
this.peoplePath = peoplePath;
layout = createLayout();
}
private Parent createLayout() {
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(p ->
new ReadOnlyStringWrapper(
p.getValue().firstName()
).getReadOnlyProperty()
);
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setCellValueFactory(p ->
new ReadOnlyStringWrapper(
p.getValue().lastName()
).getReadOnlyProperty()
);
//noinspection unchecked
tableView.getColumns().setAll(firstNameCol, lastNameCol);
tableView.setPrefHeight(150);
Button save = new Button("Save");
save.setOnAction(this::save);
Button clear = new Button("Clear");
clear.setOnAction(this::clear);
Button load = new Button("Load");
load.setOnAction(this::load);
Button restoreDefault = new Button("Default Data");
restoreDefault.setOnAction(this::restoreDefault);
HBox controls = new HBox(10, save, clear, load, restoreDefault);
VBox layout = new VBox(10, controls, tableView);
layout.setPadding(new Insets(10));
return layout;
}
public Parent getLayout() {
return layout;
}
private void save(ActionEvent e) {
try {
listSerializer.serializeList(
peoplePath,
new ArrayList<>(
tableView.getItems()
)
);
System.out.println("Saved to: " + peoplePath);
System.out.println(Files.readString(peoplePath));
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void clear(ActionEvent e) {
tableView.getItems().clear();
System.out.println("Cleared data in UI");
}
private void load(ActionEvent e) {
try {
if (!peoplePath.toFile().exists()) {
tableView.getItems().clear();
System.out.println("Saved data file does not exist, clearing data: " + peoplePath);
return;
}
tableView.getItems().setAll(
listSerializer.deserializeList(peoplePath)
);
System.out.println("Loaded data from: " + peoplePath);
System.out.println(Files.readString(peoplePath));
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void restoreDefault(ActionEvent e) {
tableView.getItems().setAll(TEST_PEOPLE);
System.out.println("Restored default data in UI");
}
}