json.Marshal 从不 panic,只返回 error;真正导致崩溃的是忽略 error 后使用 nil 切片、空指针或类型断言失败,正确做法是立即检查并处理 error。
json.Marshal 会 panic?它其实不会很多人以为 json.Marshal 可能 panic,实际它**从不 panic**,而是返回 error。真正让你程序崩溃的,往往是忽略这个 error 后继续用 nil 的字节切片(比如传给 HTTP 响应或写文件),或在后续逻辑中触发空指针/类型断言失败。
json.Marshal 只在遇到不可序列化值时返回非 nil 错误,例如含 func、chan、未导出字段且无 json tag、循环引用等*json.UnsupportedTypeError 或 *json.InvalidUTF8Error 等,都实现了 error 接口,可直接判断recover() 拦截 json.Marshal —— 它根本不会抛出 panicjson.Marshal 错误的惯用写法Go 中标准做法是「检查 error,立刻处理」。常见错误是只打印日志却不终止流程,导致下游收到空数据或乱码。
data := struct {
Name string `json:"name"`
Age int `json:"age"`
Fn func() `json:"-"` // 不导出 + 无 tag → 会出错
}{
Name: "Alice",
Age: 30,
}
b, err := json.Marshal(data)
if err != nil {
// ✅ 正确:区分错误类型,做针对性响应
switch e := err.(type) {
case *json.UnsupportedTypeError:
log.Printf("无法序列化字段 %s: %v", e.Type.String(), e.Error())
http.Error(w, "响应数据构造失败", http.StatusInternalServerError)
return
case *json.InvalidUTF8Error:
log.Printf("字符串含非法 UTF-8: %v", e)
http.Error(w, "数据编码异常", http.StatusBadRequest)
return
default:
log.Printf("JSON 序列化未知错误: %v", err)
http.Error(w, "服务内部错误", http.StatusInternalServerError)
return
}
}
w.Header().Set("Content-Type", "application/json")
w.Write(b)
json.Marshal 返回 error?不是所有 Go 类型都能直译成 JSON。以下情况必然失败,且错误信息明确:
func、chan、complex64/complex128 类型字段(即使已导出)json:"..." tag;若加了 json:"-" 则跳过,不报错string、int、int32 等基本可比较类型(如 map[struct{X int}]string)json: unsupported value: encountered a cycle
json.Marshal 错误的实用技巧线上环境难复现循环引用或非法 UTF-8,建议在单元测试中主动构造边界 case。
reflect.ValueOf(v).Kind() == reflect.Func 提前过滤掉含函数字段的结构体(适合封装自己的 SafeMarshal)utf8.Va
lid([]byte(s)) 预检,避免传入 json.Marshal 后才报错go test -v 并在测试中故意传入含 chan int 的结构体,确认错误分支被覆盖json.Marshal(nil) 是合法的,返回字面量 null,不会出错最常被忽略的是:错误处理后忘记 return,导致仍执行后续 w.Write(nil) —— 这会让客户端收到空响应而不报错,排查难度远大于直接返回 500。