Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Сохранение заметок в JSON

Пока заметки живут только в памяти: закрыли окно — всё пропало. Сохраним их в файл в формате JSON. Писать JSON вручную не будем — это делает библиотека Jackson: она превращает список записей Note в текст и обратно. В курсе используется Jackson 3 — его пакеты начинаются с tools.jackson, а записи Java он понимает без настроек.

Зависимость и модуль

Добавьте в pom.xml:

<!-- pom.xml — новая зависимость -->
<dependency>
    <groupId>tools.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>3.2.3</version>
</dependency>

И подключите модуль Jackson в module-info.java:

// src/main/java/module-info.java
module com.example.notes {
    requires javafx.controls;
    requires javafx.fxml;
    requires tools.jackson.databind;
 
    opens com.example.notes to javafx.fxml;
    exports com.example.notes;
}

Запись Note публичная и лежит в экспортированном пакете, поэтому Jackson читает её через обычные методы title(), text() и публичный конструктор.

Класс хранилища

Работу с файлом вынесем в отдельный класс — контроллеру незачем знать, как устроен JSON:

// src/main/java/com/example/notes/NoteStorage.java
package com.example.notes;
 
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.SerializationFeature;
import tools.jackson.databind.json.JsonMapper;
 
public class NoteStorage {
 
    private final Path file;
    private final JsonMapper mapper = JsonMapper.builder()
            .enable(SerializationFeature.INDENT_OUTPUT)
            .build();
 
    public NoteStorage(Path file) {
        this.file = file;
    }
 
    public List<Note> load() {
        if (Files.notExists(file)) {
            return List.of();
        }
        return mapper.readValue(file.toFile(), new TypeReference<List<Note>>() {});
    }
 
    public void save(List<Note> notes) {
        mapper.writeValue(file.toFile(), notes);
    }
}

JsonMapper — главный объект Jackson. INDENT_OUTPUT включает отступы, чтобы файл было удобно читать. TypeReference нужен, чтобы Jackson знал тип элементов списка: из-за стирания типов List.class этого не сообщает. Ошибки чтения в Jackson 3 — непроверяемые исключения, поэтому throws не нужен.

Подключение к контроллеру

Демонстрационные заметки больше не нужны — список начинается пустым и заполняется из файла:

// src/main/java/com/example/notes/MainController.java — поля и начало initialize()
private final ObservableList<Note> notes = FXCollections.observableArrayList();
private final NoteStorage storage = new NoteStorage(
        Path.of(System.getProperty("user.home"), "notes.json"));
 
// в initialize(), после table.setItems(notes):
notes.setAll(storage.load());
notes.addListener((ListChangeListener<Note>) change -> storage.save(notes));

Файл лежит в домашней папке пользователя: C:\Users\Имя\notes.json в Windows, /home/имя/notes.json в Linux. setAll заменяет содержимое списка загруженным. Слушатель срабатывает при каждом добавлении, удалении и замене, поэтому отдельная кнопка «Сохранить» не нужна. Приведение к ListChangeListener подсказывает компилятору, какой из двух методов addListener вызвать.

Проверка

Запустите приложение: таблица пуста, «Заметок: 0». Добавьте «Купить молоко» с текстом «2 литра» и флажком «Важная», затем «Позвонить Олегу». В домашней папке появится notes.json:

[ {
  "title" : "Купить молоко",
  "text" : "2 литра",
  "category" : "Идеи",
  "important" : true
}, {
  "title" : "Позвонить Олегу",
  "text" : "",
  "category" : "Идеи",
  "important" : false
} ]

Закройте окно и запустите приложение снова — обе заметки на месте.

Частая ошибка

Если файл испорчен, например его поправили вручную и оставили лишнюю запятую, load() бросает исключение, и окно не открывается: tools.jackson.core.exc.StreamReadException: Unexpected character ('}' (code 125)): was expecting double-quote to start property name. В следующем уроке загрузка переедет в фоновую задачу, и такая ошибка станет сообщением в строке состояния, а не падением программы.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.