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
相关产品推荐
相关产品推荐

