应使用 json.Valid 函数校验 JSON 合法性,它仅做语法扫描、不分配内存、不触发 Unmarshal,返回 bool 且对 nil/空切片安全;重复 key 视为合法,未闭合结构等语法错误才返回 false。

JSON反序列化时panic: invalid character报错怎么定位
Go 的 json.Unmarshal 遇到非法字符直接 panic,不带行号也不提示上下文,这是最让人抓狂的点。它不是校验失败,是解析器在词法层面就崩了——比如开头多了个 BOM、结尾多了个逗号、字符串里混了未转义的换行。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 先用
bytes.HasPrefix(data, []byte("\xef\xbb\xbf"))检查 UTF-8 BOM,有就切掉再解 - 用
json.RawMessage做“懒加载”,把原始字节先存下来,等真正需要字段时再解,避免一上来就 panic - 临时加一行
fmt.Printf("raw: %q\n", data),看输出里有没有肉眼可见的乱码、控制字符或非 ASCII 空格(比如 \u200b) - 别信前端传来的 Content-Type:即使标了
application/json,body 也可能是 HTML 错误页或空字符串
想校验 JSON 合法性但不想 panic,该用哪个函数
别碰 json.Unmarshal 做校验——它设计目标是转换,不是验证。真要判断“是不是合法 JSON”,唯一轻量可靠的方式是调用 json.Valid,它只做语法扫描,不分配结构体,不触发任何 Unmarshal 行为。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
-
json.Valid(data)返回 bool,输入 nil 或空 slice 也安全,适合前置守门 - 注意它不检查语义:
{"a":1,"a":2}(重复 key)和{"a":}(语法错误)结果不同——前者返回 true,后者 false - 如果后续还要 Unmarshal,别两次读 data:先
json.Valid再json.Unmarshal是冗余 IO,直接用json.Unmarshal+ error 判断更高效 - 第三方库如
go-json的Validate更快,但标准库够用,除非你压测发现json.Valid成瓶颈
struct tag 里 omitempty 导致空字段消失,后端校验逻辑崩了怎么办
这不是 JSON 格式错误,但常被误认为“解析失败”:前端传 {"name":"foo","age":null},Go struct 定义 Age *int `json:"age,omitempty"`,结果 json.Unmarshal 后 Age 是 nil,但业务逻辑以为字段被省略 = 不提供,实际是“明确设为 null”。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 去掉
omitempty,改用指针 + 显式零值判断:接收时允许"age":null,解出*int为 nil,业务层再区分“没传”和“传了 null” - 如果必须保留 omitempty,前端协议就得约定:null 字段必须显式传
"age": null,且后端文档写死这条,否则前后端对“字段缺失”的理解永远错位 - 用自定义
UnmarshalJSON方法拦截,对特定字段做 null 感知:比如检测到json.RawMessage是null字面量,就设一个特殊哨兵值
中文字段名或特殊符号导致解析失败,是不是编码问题
只要 JSON 字符串本身是合法 UTF-8,字段名含中文、emoji、下划线甚至 emoji 组合都没问题——json.Unmarshal 对 key 名字完全不敏感。真正卡住的,往往是 struct tag 写错了,或者字段类型不匹配。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 检查 struct tag 是否漏了
json:"xxx",Go 默认按字段名匹配,而 Go 字段不能以中文开头,所以没 tag 就必然失败 - 字段类型写成
string却收到数字:如{"score":95}对应Score string,会报json: cannot unmarshal number into Go struct field Score of type string - 字段名大小写不一致:tag 写成
json:"UserName",但 JSON 里是"username",就匹配不上,字段保持零值,不报错也不 panic - 用
json.Compact预处理输入:把换行缩进全干掉,能排除部分因格式混乱引发的误判










