
本文详解如何在 Java 中安全地向 JSON 文件的 `"players"` 数组中追加新玩家对象,避免因误用文件追加模式导致 JSON 结构损坏(如重复根对象、非法多文档 JSON)。
在 Java 开发中,使用 JSON 文件持久化简单数据(如玩家列表)很常见,但一个典型陷阱是:误将 FileOutputStream(..., true)(追加模式)与“读取-修改-写入”逻辑混用。这正是你遇到问题的根本原因——当前代码每次调用 addPlayer() 时:
- ✅ 正确解析现有 data.json 内容(获取 players 数组);
- ✅ 正确向该数组添加新玩家;
- ❌ 却以 追加模式(true)打开文件并直接 println(jsonObject) → 导致新 JSON 对象被写在原文件末尾,形成多个独立 JSON 文档(非法 JSON),而非更新原有结构。
? 正确做法:读取 → 修改 → 覆盖写入
必须使用覆盖模式(false 或省略第二个参数) 重写整个文件。以下是修正后的核心逻辑(基于 org.json 库):
public void addPlayer(PlayerManager playerManager) {
File file = new File("data.json");
JSONObject jsonObject;
JSONArray jsonArray;
// 1. 读取现有 JSON(若文件不存在则初始化空对象)
if (file.exists()) {
try (FileReader reader = new FileReader(file)) {
jsonObject = (JSONObject) new JSONParser().parse(reader);
jsonArray = jsonObject.has("players")
? (JSONArray) jsonObject.get("players")
: new JSONArray();
} catch (Exception e) {
System.err.println("解析 JSON 失败,将重建文件");
jsonObject = new JSONObject();
jsonArray = new JSONArray();
}
} else {
jsonObject = new JSONObject();
jsonArray = new JSONArray();
}
// 2. 向 players 数组添加新玩家
ArrayUnorderedList playerList = playerManager.getPlayersList();
for (Players player : playerList) {
JSONObject playerObj = new JSONObject();
playerObj.put("name", player.getName());
playerObj.put("team", player.getTeam().toString());
playerObj.put("level", player.getLevel());
playerObj.put("experiencePoints", player.getExperiencePoints());
playerObj.put("current_Energy", player.getCurrentEnergy());
jsonArray.add(playerObj);
}
// 3. 更新 JSON 根对象,并**覆盖写入文件**
jsonObject.put("players", jsonArray);
try (PrintWriter writer = new PrintWriter(new FileWriter(file))) {
writer.write(jsonObject.toString(2)); // 2 表示缩进 2 空格,提升可读性
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
} ⚠️ 关键注意事项
- 永远不要对同一 JSON 文件同时启用 append=true 并执行“读-改-写”:追加模式仅适用于日志等纯追加场景,不适用于结构化数据更新。
- 使用 try-with-resources:确保 FileReader 和 FileWriter 自动关闭,避免资源泄漏。
- 健壮性处理:捕获 ParseException 和 IOException,并在解析失败时优雅降级(如初始化空结构)。
- JSON 格式化:jsonObject.toString(2) 输出带缩进的格式化 JSON,便于调试和人工阅读。
- 线程安全:若多线程调用 addPlayer(),需加同步(如 synchronized 块或 ReentrantLock),否则可能因并发读写导致数据丢失。
✅ 验证效果
执行多次 addPlayer() 后,data.json 将严格保持单个合法 JSON 对象结构:
立即学习“Java免费学习笔记(深入)”;
{
"players": [
{ "name": "tenente", "team": "Giants", ... },
{ "name": "tenente", "team": "Giants", ... }
]
}而非出现多个 {...}{...} 的非法拼接。
通过遵循“读取→内存修改→覆盖写入”的标准流程,即可彻底解决 JSON key 重复、结构错乱的问题,确保数据持久化既可靠又符合 JSON 规范。










