根本原因是json.encoder底层writer存在缓冲,数据未刷入磁盘;必须确保调用file.close()(触发刷新)且检查encode()返回的error,避免panic或提前return导致丢失。

为什么直接用 json.Encoder 写文件会丢失数据
常见现象是:调用 encoder.Encode() 后文件为空,或只写入了部分 JSON。根本原因是 json.Encoder 的底层 Writer(比如 *os.File)有缓冲,数据还在内存里没刷到磁盘。
必须显式调用 file.Close() 或 file.Sync() 才能落盘——但更稳妥的做法是在 Close() 前确保 Encoder 自身没出错,且缓冲已清空。
-
json.Encoder不提供Flush()方法,它依赖底层Writer的行为 - 如果用
bufio.NewWriter(file)包一层,必须手动调用writer.Flush() - 直接传
*os.File给json.NewEncoder()时,file.Close()会触发刷新,但若中途 panic 或提前 return,就漏了
如何安全地把结构体写进 JSON 文件
核心原则:用 defer file.Close() + 检查 Encode() 返回的 error,别省略任何一步。
典型错误是只检查 os.OpenFile() 是否成功,却忽略 Encode() 可能返回的序列化错误(比如含不可序列化字段)。
- 打开文件用
os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644),避免覆盖风险 - 用
json.NewEncoder(file).Encode(v),不是json.Marshal()+Write()——后者多一次内存拷贝 - 务必检查
Encode()的 error;即使结构体合法,IO 错误(如磁盘满)也会在这里暴露 - 不用手动
file.Sync(),Close()已包含 flush 和 sync 逻辑
file, err := os.OpenFile("data.json", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
enc := json.NewEncoder(file)
if err := enc.Encode(myStruct); err != nil {
log.Fatal(err) // 这里可能报 "json: unsupported type: map[interface {}]interface {}"
}
写多个对象到同一文件时要注意换行和分隔
如果循环调用 Encode() 往一个文件写多个 JSON 值(如日志流、NDJSON),默认输出是紧凑无分隔的,会导致解析失败。
json.Encoder 本身不加换行,但人眼可读和工具解析都需要每条记录独占一行(即 NDJSON 格式)。
- 每次
Encode()后手动写\n:用file.Write([]byte{'\n'}) - 不要用
fmt.Fprintln(file, ""),它可能写\r\n(Windows 下)破坏 JSON 格式 - 若需格式化输出(带缩进),用
enc.SetIndent("", " "),但它会影响性能,仅调试用 - 注意:多次
Encode()不能直接拼成一个合法 JSON 数组,那是不同格式(NDJSON ≠ JSON array)
替换 json.Encoder 的轻量场景:什么时候该用 json.Marshal()
小数据(json.Marshal() + os.WriteFile() 更简洁,也更容易测试。
但要注意:Marshal() 把整个结果加载进内存,而 Encoder 是流式写入,适合大数据或内存受限环境。
-
os.WriteFile("x.json", data, 0644)自动处理 close 和 sync,比手写OpenFile+Write+Close更少出错 - 如果结构体可能很大,或要写几十万条记录,坚持用
Encoder+bufio.Writer套一层提升吞吐 -
json.Encoder对io.Writer接口友好,方便单元测试(比如传bytes.Buffer)
真正容易被忽略的是:无论用哪种方式,只要涉及文件 IO,就必须检查每一步的 error——Go 不会帮你吞掉磁盘满、权限拒绝或路径不存在这些现实问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











