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

Java+GSON实现JSON文件新增/覆写日记条目技术问询

嘿,我来帮你搞定这个JavaFX日记项目的JSON存储问题,还有后续数据库兼容的方案~

一、先解决JSON写入的核心问题:新增/覆写条目

你的核心需求是保留现有条目,只针对指定日期做新增或覆写,所以不能直接覆盖整个JSON文件,得先读入现有内容,更新后再写回。我用Jackson(Java生态最常用的JSON库)来给你写示例代码:

1. 优化Entry实体类(适配JSON映射)

import com.fasterxml.jackson.annotation.JsonProperty;

public class DiaryEntry {
    @JsonProperty("textfield1")
    private String textField1;
    @JsonProperty("textfield2")
    private String textField2;
    @JsonProperty("Slider")
    private int sliderValue;

    // 必须要有无参构造(Jackson反序列化需要)
    public DiaryEntry() {}

    public DiaryEntry(String textField1, String textField2, int sliderValue) {
        this.textField1 = textField1;
        this.textField2 = textField2;
        this.sliderValue = sliderValue;
    }

    // 生成所有属性的getter和setter
    public String getTextField1() { return textField1; }
    public void setTextField1(String textField1) { this.textField1 = textField1; }
    public String getTextField2() { return textField2; }
    public void setTextField2(String textField2) { this.textField2 = textField2; }
    public int getSliderValue() { return sliderValue; }
    public void setSliderValue(int sliderValue) { this.sliderValue = sliderValue; }
}

2. 重写JSONWriter的核心逻辑

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class JSONWriter {
    private static final ObjectMapper objectMapper = new ObjectMapper();
    // 可以自定义文件路径,比如放在项目根目录的diary.json
    private static final String DIARY_FILE_PATH = "diary.json";

    // 写入/覆写指定日期的条目
    public static void writeEntry(String date, DiaryEntry entry) throws IOException {
        // 1. 读取现有JSON内容,文件不存在则初始化空Map
        Map<String, DiaryEntry> diaryMap;
        File diaryFile = new File(DIARY_FILE_PATH);
        if (diaryFile.exists()) {
            diaryMap = objectMapper.readValue(
                diaryFile,
                objectMapper.getTypeFactory().constructMapType(Map.class, String.class, DiaryEntry.class)
            );
        } else {
            diaryMap = new HashMap<>();
        }

        // 2. 新增或覆写日期对应的条目(Map的put方法天然支持:存在则替换,不存在则添加)
        diaryMap.put(date, entry);

        // 3. 格式化写回文件(方便阅读)
        objectMapper.writerWithDefaultPrettyPrinter().writeValue(diaryFile, diaryMap);
    }

    // 按日期读取条目(满足后续过滤需求)
    public static DiaryEntry readEntryByDate(String date) throws IOException {
        File diaryFile = new File(DIARY_FILE_PATH);
        if (!diaryFile.exists()) return null;
        
        Map<String, DiaryEntry> diaryMap = objectMapper.readValue(
            diaryFile,
            objectMapper.getTypeFactory().constructMapType(Map.class, String.class, DiaryEntry.class)
        );
        return diaryMap.get(date);
    }

    // 可选:读取所有条目(方便后续扩展批量操作)
    public static Map<String, DiaryEntry> readAllEntries() throws IOException {
        File diaryFile = new File(DIARY_FILE_PATH);
        return diaryFile.exists() ? 
            objectMapper.readValue(
                diaryFile,
                objectMapper.getTypeFactory().constructMapType(Map.class, String.class, DiaryEntry.class)
            ) : new HashMap<>();
    }
}

3. JavaFX控件中调用示例

比如保存按钮的点击逻辑:

import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

// 假设你已经初始化了这些控件
TextField textField1 = new TextField();
TextField textField2 = new TextField();
Slider slider = new Slider(0, 5, 2);
Button saveBtn = new Button("保存日记");

saveBtn.setOnAction(e -> {
    // 用标准ISO日期格式(比如2024-05-20)作为键
    String date = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
    DiaryEntry entry = new DiaryEntry(
        textField1.getText(),
        textField2.getText(),
        (int) slider.getValue()
    );

    try {
        JSONWriter.writeEntry(date, entry);
        new Alert(Alert.AlertType.INFORMATION, "日记保存成功!").showAndWait();
    } catch (IOException ex) {
        new Alert(Alert.AlertType.ERROR, "保存失败:" + ex.getMessage()).showAndWait();
        ex.printStackTrace();
    }
});

按日期加载的逻辑:

TextField dateInput = new TextField();
Button loadBtn = new Button("加载日记");

loadBtn.setOnAction(e -> {
    String date = dateInput.getText().trim();
    if (date.isEmpty()) {
        new Alert(Alert.AlertType.WARNING, "请输入日期(格式:YYYY-MM-DD)").showAndWait();
        return;
    }

    try {
        DiaryEntry entry = JSONWriter.readEntryByDate(date);
        if (entry != null) {
            textField1.setText(entry.getTextField1());
            textField2.setText(entry.getTextField2());
            slider.setValue(entry.getSliderValue());
        } else {
            new Alert(Alert.AlertType.INFORMATION, "该日期无日记条目").showAndWait();
            // 清空控件
            textField1.clear();
            textField2.clear();
            slider.setValue(0);
        }
    } catch (IOException ex) {
        new Alert(Alert.AlertType.ERROR, "加载失败:" + ex.getMessage()).showAndWait();
        ex.printStackTrace();
    }
});

二、兼容后续数据库的存储方案推荐

为了以后能平滑切换到数据库(比如SQLite、MySQL),核心思路是抽象数据访问层(DAO),把存储逻辑和业务逻辑彻底解耦。

1. 定义DAO接口(统一操作规范)

import java.io.IOException;
import java.util.Map;

public interface DiaryDao {
    void saveEntry(String date, DiaryEntry entry) throws Exception;
    DiaryEntry getEntryByDate(String date) throws Exception;
    Map<String, DiaryEntry> getAllEntries() throws Exception;
    // 后续可以添加删除、修改等方法
}

2. 实现JSON版DAO(复用之前的逻辑)

public class JsonDiaryDao implements DiaryDao {
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final String DIARY_FILE_PATH = "diary.json";

    @Override
    public void saveEntry(String date, DiaryEntry entry) throws IOException {
        Map<String, DiaryEntry> diaryMap = getAllEntries();
        diaryMap.put(date, entry);
        objectMapper.writerWithDefaultPrettyPrinter().writeValue(new File(DIARY_FILE_PATH), diaryMap);
    }

    @Override
    public DiaryEntry getEntryByDate(String date) throws IOException {
        return getAllEntries().get(date);
    }

    @Override
    public Map<String, DiaryEntry> getAllEntries() throws IOException {
        File diaryFile = new File(DIARY_FILE_PATH);
        return diaryFile.exists() ? 
            objectMapper.readValue(
                diaryFile,
                objectMapper.getTypeFactory().constructMapType(Map.class, String.class, DiaryEntry.class)
            ) : new HashMap<>();
    }
}

3. 后续切换到数据库时,只需要新增数据库版DAO

比如用SQLite的示例(需要引入sqlite-jdbc依赖):

import java.sql.*;
import java.util.HashMap;
import java.util.Map;

public class SqliteDiaryDao implements DiaryDao {
    private static final String DB_URL = "jdbc:sqlite:diary.db";

    // 初始化数据库表
    public SqliteDiaryDao() {
        try (Connection conn = DriverManager.getConnection(DB_URL)) {
            String createTableSql = "CREATE TABLE IF NOT EXISTS diary (" +
                    "date TEXT PRIMARY KEY," +
                    "textfield1 TEXT," +
                    "textfield2 TEXT," +
                    "slider_value INTEGER)";
            try (Statement stmt = conn.createStatement()) {
                stmt.execute(createTableSql);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void saveEntry(String date, DiaryEntry entry) throws SQLException {
        // REPLACE关键字天然支持:存在则覆写,不存在则新增
        String sql = "REPLACE INTO diary (date, textfield1, textfield2, slider_value) VALUES (?, ?, ?, ?)";
        try (Connection conn = DriverManager.getConnection(DB_URL);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, date);
            pstmt.setString(2, entry.getTextField1());
            pstmt.setString(3, entry.getTextField2());
            pstmt.setInt(4, entry.getSliderValue());
            pstmt.executeUpdate();
        }
    }

    @Override
    public DiaryEntry getEntryByDate(String date) throws SQLException {
        String sql = "SELECT * FROM diary WHERE date = ?";
        try (Connection conn = DriverManager.getConnection(DB_URL);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, date);
            ResultSet rs = pstmt.executeQuery();
            if (rs.next()) {
                return new DiaryEntry(
                        rs.getString("textfield1"),
                        rs.getString("textfield2"),
                        rs.getInt("slider_value")
                );
            }
            return null;
        }
    }

    @Override
    public Map<String, DiaryEntry> getAllEntries() throws SQLException {
        Map<String, DiaryEntry> map = new HashMap<>();
        String sql = "SELECT * FROM diary";
        try (Connection conn = DriverManager.getConnection(DB_URL);
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {
            while (rs.next()) {
                String date = rs.getString("date");
                DiaryEntry entry = new DiaryEntry(
                        rs.getString("textfield1"),
                        rs.getString("textfield2"),
                        rs.getInt("slider_value")
                );
                map.put(date, entry);
            }
        }
        return map;
    }
}

4. 业务代码中依赖接口而非具体实现

这样切换存储方式时,只需要修改一行代码:

// 现在用JSON存储
DiaryDao diaryDao = new JsonDiaryDao();

// 以后换成SQLite,只需要改这一行
// DiaryDao diaryDao = new SqliteDiaryDao();

// 保存逻辑和之前完全一样,不需要修改
saveBtn.setOnAction(e -> {
    String date = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
    DiaryEntry entry = new DiaryEntry(textField1.getText(), textField2.getText(), (int) slider.getValue());
    try {
        diaryDao.saveEntry(date, entry);
        new Alert(Alert.AlertType.INFORMATION, "保存成功").showAndWait();
    } catch (Exception ex) {
        new Alert(Alert.AlertType.ERROR, "保存失败:" + ex.getMessage()).showAndWait();
    }
});

最后补充说明

  • 如果用Jackson,需要在Maven/Gradle中添加依赖:
    Maven:
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.15.2</version>
    </dependency>
    
  • 日期格式务必统一,推荐用YYYY-MM-DD,避免解析错误。
  • 确保程序对JSON文件/数据库文件有读写权限。

内容的提问来源于stack exchange,提问作者Rain ReapzZ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:17:48