I want the method to append a new person object according to the form each time the save button is clicked, and read the new content of the file each time the retrieve button is clicked, unfortunately it only reads the last entered person.
am now here for help

package sample;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import java.io.*;
import java.net.URL;
import java.time.LocalDate;
import java.util.ResourceBundle;

public class Controller implements Initializable {

    @FXML
    private Button save, retrieve;
    @FXML
    private TextField first, last;
    @FXML
    private Label saved, retrieved;
    @FXML
    private DatePicker date;
    @Override
    public void initialize(URL location, ResourceBundle resources) {

        //System.out.println(date.getValue());

        save.setOnAction(e->{
            savePerson();
        });

        retrieve.setOnAction(e->{
            retrievePeople();
        });

    }

    public void savePerson() {

        if (first.getText().isEmpty() || last.getText().isEmpty()) {
            saved.setText("Please enter all fields");
        } else {
            String firstName = first.getText().trim();
            String lastName = last.getText().trim();
            LocalDate birthDate = date.getValue();

        try {
            //create new instance of the newClass.
            Person person = new Person(firstName, lastName, birthDate);

            //write object to the file.
            FileOutputStream fos = new FileOutputStream("file.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(person);
            oos.flush();
            oos.close();
            fos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

            saved.setText("NEw person saved");

        }

    }

    public void retrievePeople(){
        try{
            boolean eof = true;
            //read the saved objects from the file
            FileInputStream fis = new FileInputStream("file.ser");
            ObjectInputStream ois = new ObjectInputStream(fis);

            while(eof){
                Person result = (Person) ois.readObject();
                System.out.println(result.getFirstName() + "  " + result.getLastName() + "  " + result.getDateOfBirth());

                eof = false;
            }ois.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        retrieved.setText("Data retrieved");
    }
}

Dani AI

Generated

— the immediate problem in the posted code is twofold: the retrieval loop only ever reads one object, and the program needs a consistent file format for multiple Person entries. ’s suggestion to treat the stored data as a single collection is a robust option; an alternative is a careful append strategy. File I/O should also run off the JavaFX application thread (use a Task) so the UI does not freeze.

A safe pattern to read many objects written sequentially is to loop until EOFException and collect into a List:

List<Person> people = new ArrayList<>();
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file.ser"))) {
    while (true) {
        try {
            people.add((Person) ois.readObject());
        } catch (EOFException eof) {
            break;
        }
    }
} catch (IOException | ClassNotFoundException ex) {
    ex.printStackTrace();
}

This only works if the file actually contains a sequence of Person objects.

If appending raw objects is required, use a small ObjectOutputStream subclass that suppresses the header when appending, and choose which stream to create based on file size:

private static class AppendableObjectOutputStream extends ObjectOutputStream {
    AppendableObjectOutputStream(OutputStream out) throws IOException { super(out); }
    @Override protected void writeStreamHeader() throws IOException { /* skip header */ }
}

// when saving:
boolean append = Files.exists(path) && Files.size(path) > 0;
try (FileOutputStream fos = new FileOutputStream("file.ser", true);
     ObjectOutputStream oos = append
         ? new AppendableObjectOutputStream(fos)
         : new ObjectOutputStream(fos)) {
    oos.writeObject(person);
}

Caveat: that append approach is more fragile (handle consistent types, avoid mixing list-serialization with appended objects).

For most simple apps the cleanest solution is what recommended: keep a List<Person> in memory (load it once), add new entries, then overwrite the file with that single List object. Ensure Person implements Serializable with a serialVersionUID, use try-with-resources, handle the first-run file-not-found case, and perform file operations on a background Task while updating UI labels via Platform.runLater.

When you write the new Person to the output file you replace/overwrite the previous contents of the file, so the file only ever contains the last Person.
You can't simply append to the file because an object file has a header, so you end up with multiple headers scattered through the file and it doesn't work.

This old DaniWeb topic shows one possible solution, but IMHO it's a bit of a fudge and may break in the future.

A better solution (in my opinion) is to have a List<Person>. When the program starts, read that list as a single object from the object file. Append new persons to the list, then write the whole list (as a single object) back to the file. (You will need to deal with the very first run where the file won't exist yet - just leave the list empty.)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.