json.Unmarshal 返回的 error 是接口值,实际类型通常为 json.SyntaxError、json.UnmarshalTypeError 或 *json.InvalidUnmarshalError,需用 errors.As 进行类型断言以精准区分错误来源。
json.Unmarshal 返回的 error 是一个接口值,实际类型通常是 *json.SyntaxError、*json.UnmarshalTypeError 或 *json.InvalidUnmarshalError。直接用 err.Error() 看到的是人类可读字符串,但无法精准区分错误来源——比如是 JSON 格式错了,还是字段类型不匹配,还是传了 nil 指针。

正确做法是用类型断言或 errors.As 提取具体错误类型:
var data map[string]interface{}
err := json.Unmarshal([]byte(`{"name": "alice", "age": "not-a-number"}`), &data)
if err != nil {
var syntaxErr *json.SyntaxError
var typeErr *json.UnmarshalTypeError
if errors.As(err, &syntaxErr) {
fmt.Printf("JSON 语法错误,位置:%d\n", syntaxErr.Offset)
} else if errors.As(err, &typeErr) {
fmt.Printf("字段 %q 类型不匹配,期望 %v,得到 %v\n",
typeErr.Field, typeErr.Type, typeErr.Value)
}
}
Go 的 json 包默认对空 JSON 对象({})或缺失字段不做报错,而是将对应字段设为零值。但如果结构体字段是**非指针非零值类型**(如 int、string),就无法区分“字段没传”和“字段传了零值”。这在 API 请求校验中容易埋坑。
*string、*int)可区分 nil(未提供)和零值(显式提供)omitempty 标签只影响序列化,不影响反序列化行为UnmarshalJSON 方法里手动检查字段是否存在,或用第三方库如 go-playground/validator
例如:
type User struct {
Name *string `json:"name"`
Age *int `json:"age"`
}
// 解析 {"name":"bob"} 后,Name != nil,Age == nil —— 可据此判断 age 是否被提供
当 JSON 层级较深(如 {"user":{"profile":{"email":"a@b"}}}),某一层解析失败时,默认错误信息不带路径,只说“expected object but found string”,很难快速定位是 user 还是 profile 出问题。
解决方法有两种:
json.Unmarshal,错误发生时上下文明确UnmarshalJSON 方法,在每个嵌套结构里加字段名前缀,例如:return fmt.Errorf("profile: %w", err)
简单示例(分步):
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
if profileBytes, ok := raw["profile"]; ok {
var profile Profile
if err := json.Unmarshal(profileBytes, &profile); err != nil {
return fmt.Errorf("failed to unmarshal 'profile': %w", err)
}
}
json.RawMessage 常用于跳过中间层解析,把某段 JSON 字节原样保留,后续按需解析。但它不是“万能兜底”——如果原始 JSON 不合法(比如多了一个逗号),Unmarshal 仍会失败;而且它只是字节切片别名,**不会做内存拷贝**,如果原始数据被复用或修改,可能导致解析结果意外变化。
json.Unmarshal 后立刻处理或拷贝 json.RawMessage 内容append([]byte(nil), raw...) 先拷贝一份典型误用:
var msg struct {
Data json.RawMessage `json:"data"`
}
json.Unmarshal(input, &msg)
// 此时 msg.Data 指向 input 的某段内存 —— 如果 input 被重用,msg.Data 可能失效
解析 JSON 错误真正难的不是捕获 error,而是让错误信息带上上下文、让类型判断稳定可靠、让字段语义清晰可验证。尤其在微服务间 JSON 协议频繁变更时,靠 err.Error() 打日志基本等于放弃排查。