json.Unmarshal必须传指针(如&config),否则因Go值传递导致字段全为零值;结构体字段须首字母大写并配json:"key"标签,缺一即静默失败。

json.Unmarshal 必须传指针,否则字段全为零值
传错参数类型是新手最常踩的坑:直接传结构体变量(如 json.Unmarshal(data, config))不会报错,但所有字段都保持初始值。Go 的 Unmarshal 需要可修改的内存地址,必须传指针(&config)。
常见错误现象:config.Port 一直是 0,config.Host 始终为空字符串,而 err == nil 让人误以为成功。
- 结构体变量定义后,立刻用
new(Config)或&Config{}获取指针 - 如果用
var c Config,后续必须写json.Unmarshal(data, &c),不能漏掉& - 嵌套结构体同理:内层字段也要靠指针传递才能被赋值,比如
Server *ServerConfig比Server ServerConfig更容错(null 时不会 panic)
字段名大小写 + json tag 缺一不可
Go 只能访问首字母大写的导出字段,而 JSON 键名通常小写或含连字符——这两者必须靠 json:"key" 标签对齐,漏掉任一环,该字段就静默失效。
错误写法:host string `json:"host"` → 字段 host 小写,Unmarshal 完全跳过;正确写法:Host string `json:"host"`。
立即学习“go语言免费学习笔记(深入)”;
- JSON 键是
"max-retries",标签必须写成json:"max-retries",写成json:"max_retries"或漏掉标签都会导致字段为空 - 嵌套结构体内层字段同样要大写+标签,例如
Address struct { City string `json:"city"` }中的City不能写成city - 想彻底忽略某个字段?用
json:"-",比注释掉字段更安全、更明确
os.ReadFile 后必须检查 BOM 和错误,不能只看 err == nil
json.Unmarshal 报错信息往往指向第一行,但真正原因是文件开头的 UTF-8 BOM(\xef\xbb\xbf)——它会让解析器把 { 当作非法字符,报出类似 invalid character '\ufeff' looking for beginning of value 的错误。
正确流程是两步检查:先确认文件读取成功,再确认字节流干净。
- 用
data, err := os.ReadFile("config.json"),然后立刻检查err:路径不存在、权限不足、磁盘满都会在这里暴露 - 接着清理 BOM:
data = bytes.TrimPrefix(data, []byte("\xef\xbb\xbf")),再传给json.Unmarshal - 即使
err == nil,也要验证关键字段是否非零:比如if config.Port == 0就该提前返回错误,而不是等下游连接失败才暴露问题
嵌套深或结构不定时,优先用 gjson 而不是硬解 map[string]interface{}
当只需要读几个深层字段(比如 "features.auth.enabled"),或者配置结构经常变,预定义 struct 成本太高——这时 gjson 比反复类型断言 map[string]interface{} 更轻、更稳。
常见翻车点:v := gjson.Get(data, "items.0.name") 返回空,可能因为 items 是空数组,或键名实际是 "Items"(大小写敏感),或值是 null 但你直接调了 v.String()。
- 永远先调
v.Exists()判断字段是否存在,再判断类型(v.IsString()、v.IsNumber()) - 数组索引越界不 panic,但
v.Exists()返回false,别假设"items.0"一定存在 - 路径区分大小写:
"Features.Auth.Enabled"和"features.auth.enabled"是两个不同路径
真正麻烦的不是语法,而是“没报错却读不到值”——这往往卡在字段未导出、标签拼错、BOM 残留、或指针传错这四个地方。盯住这四点,90% 的 JSON 解析问题当场消失。


















