正确读取JSON文件需先用os.ReadFile加载字节再json.Unmarshal,注意错误处理、BOM处理、结构体字段导出与json tag、空值类型匹配、大文件流式解析及编码转换。

直接用 json.Unmarshal 读取文件内容会 panic,必须先读文件再解码
很多人写成这样:
data := []byte("config.json")
json.Unmarshal(data, &cfg)
——这其实只是把字符串字面量当 JSON 解析了,根本没读文件。正确流程是两步:先用 os.ReadFile(Go 1.16+)或 ioutil.ReadFile(旧版)加载文件字节,再传给 json.Unmarshal。
-
os.ReadFile返回[]byte和error,必须检查错误,空文件或权限不足都会导致失败 - 不要对文件内容做任何字符串拼接或截断,
json.Unmarshal要求输入是合法 UTF-8 编码的完整 JSON 文本 - 如果 JSON 文件带 BOM(比如 Windows 记事本保存的),
json.Unmarshal会报invalid character 'ï' looking for beginning of value,需提前 strip
结构体字段必须导出且加 json: tag 才能被解码
Go 的 json 包只能设置导出字段(首字母大写),且默认按字段名匹配。如果 JSON 键名是 user_name,而结构体字段叫 UserName,不加 tag 就无法映射。
- 使用
json:"user_name"显式指定键名,支持别名、忽略字段(json:"-")、omitempty(空值不序列化) - 嵌套结构体字段也要导出,否则整个嵌套对象会被跳过,不会报错但值为零值
- 如果 JSON 中有字段在结构体里不存在,默认忽略;想捕获未知字段,可加一个
json.RawMessage类型的兜底字段
处理数组、空值、混合类型时,interface{} 不够用,优先用具体类型或自定义 UnmarshalJSON
遇到 JSON 数组如 "tags": ["go", "json"],用 []string 直接接收最安全;但如果 API 返回有时是字符串、有时是数组(常见于弱类型后端),硬转会 panic。
- 避免无脑用
map[string]interface{}或[]interface{},类型丢失后后续逻辑难维护、易出错 - 对不确定字段,定义一个字段类型实现
UnmarshalJSON([]byte) error方法,内部判断json.Unmarshal失败后尝试其他解析路径 - 空字段(
null)对应指针或sql.NullString等可空类型;普通值类型(如int)遇到null会解码失败
大文件别用 json.Unmarshal,改用 json.Decoder 流式解析
用 os.ReadFile 把几百 MB 的 JSON 一次性读进内存,不仅慢,还可能 OOM。这时应打开文件句柄,用 json.NewDecoder 边读边解。
-
json.NewDecoder(f).Decode(&v)每次只解一个顶层 JSON 值(对象或数组),适合日志行、NDJSON 格式 - 如果是单个巨型 JSON 对象,无法流式解码整个结构,但可用
decoder.Token()手动跳过不需要的字段,减少内存占用 - 注意文件编码:标准
json包只支持 UTF-8,若文件是 UTF-16,需先用golang.org/x/text/encoding转换
实际项目里最容易被忽略的是错误处理粒度——不是只检查 os.ReadFile 是否成功,而是要区分「文件不存在」、「权限拒绝」、「JSON 语法错误」、「字段类型不匹配」这四类问题,它们对应的修复方式完全不同。
