go导出json需确保字段可导出、用marshalindent美化、newencoder流式写入、处理null及错误。小写字段被忽略,数据库结果须scan后序列化,大数据量应分批或ndjson导出。

Go 原生 json.Marshal 就能导出 JSON,但直接用它写文件容易丢数据、乱编码、没换行——关键不是“能不能”,而是“怎么导才可靠”。
导出前先确认结构体字段是否可导出
Go 的 json 包只序列化首字母大写的字段(即导出字段),小写字段默认被忽略,且不会报错,容易误以为数据全导出了。
- 检查结构体:所有要导出的字段必须首字母大写,比如
ID、Name,不能是id、name - 用
json:"xxx"标签控制 key 名,比如CreatedAt time.Time `json:"created_at"` - 如果字段是私有(小写)但又必须导出,得加
json:"xxx,omitempty"并确保它有 getter 方法——但不推荐,改结构体更干净
写 JSON 文件时别直接 json.Marshal + os.WriteFile
看似一行能搞定,但会踩三个坑:中文变 Unicode 转义、无缩进难调试、错误被吞掉。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
json.MarshalIndent替代json.Marshal,加缩进和换行:json.MarshalIndent(data, "", " ") - 别用
os.WriteFile一次性写入,改用os.Create+json.NewEncoder流式写入,尤其适合大数据量,内存友好 - 务必检查
err:比如磁盘满、路径无权限、结构体含不可序列化类型(如func、chan),这些都会让Marshal返回非 nil 错误
数据库查询结果导出要过一遍 Scan 或 Rows.MapScan
直接把 *sql.Rows 传给 json.Marshal 没用——它只会输出指针地址或空对象,因为 Rows 不是数据本身。
- 老老实实用
rows.Scan逐行读到结构体或map[string]interface{}中再序列化 - 用
github.com/jmoiron/sqlx可以简化:db.Select(&results, query)直接填充切片 - 注意
NULL值:数据库里的NULL对应 Go 的sql.NullString等类型,它们默认 JSON 输出是{"Valid":false,"String":""},如需简洁 null,得自定义MarshalJSON方法
导出大文件时小心内存和性能
一次性 json.MarshalIndent 所有数据,10 万条记录可能占几百 MB 内存,还卡住 goroutine。
- 用
json.NewEncoder(file).Encode(item)逐条写入,配合file.WriteString("\n")实现 NDJSON(每行一个 JSON)格式,适合日志类导出 - 如果必须单个 JSON 数组,就分批查、分批 Marshal、拼接写入——别全 load 到内存
- 避免在循环里反复创建
bytes.Buffer或strings.Builder,复用实例减少 GC 压力
最常被忽略的是错误处理和 NULL 处理:导出成功但内容为空,大概率是字段没导出或 sql.NullXxx.Valid == false 被静默跳过。多打一行 fmt.Printf("len: %d\n", len(data)) 能省半天排查时间。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










