json.Unmarshal对null“静默失效”是因为反射仅执行赋值,不决定语义;null处理取决于字段类型(如*string可设nil,string只能留零值)和初始状态,且无法通过反射区分null与字段缺失。

Go 的反射机制本身不处理 null;json.Unmarshal 内部用反射写值,但 null 的语义控制权在类型和初始化状态上——不是反射能绕开的。
为什么 json.Unmarshal 对 null “静默失效”
反射只是工具,它不会替你决定 null 该变成什么。真正起作用的是字段类型 + 初始值:
-
json.Unmarshal遇到null时,只对 nil 指针字段 执行赋 nil 操作;若指针已初始化(比如指向一个空字符串),null就被忽略 - 非指针字段(如
string、int)根本无法表示null,null进来就跳过,留零值 - 反射过程里,
reflect.Value.SetNil()只会在字段是导出的指针且当前为 nil 时被调用;否则连这步都不会走
嵌套结构中指针字段为 null 时的反射陷阱
当 JSON 是 {"user": {"name": null}},结构体定义为 type Resp struct { User *User },问题不在反射逻辑,而在你是否让 User 字段可被正确覆盖:
- 如果
resp.User已指向一个&User{}实例,json.Unmarshal不会重置它——即使 JSON 中"user"是null - 必须确保嵌套指针字段初始为
nil,否则null进来等于没发生 - 从
sync.Pool复用结构体时,务必手动清零所有指针字段:v.User = nil,不能依赖反射自动“还原”
想用反射统一处理 null?先放弃这个念头
有人试图用 reflect.StructOf 动态构造结构体并加自定义 UnmarshalJSON 方法,但实际踩坑更多:
立即学习“go语言免费学习笔记(深入)”;
- 反射构造的类型无法直接绑定方法——
UnmarshalJSON必须提前定义在命名类型上,不能靠反射“注入” - 若字段类型是
*string,你仍得在每个字段上做if !field.IsNil() { ... }判断,反射不帮你省这一步 - 真正需要动态控制 null 语义的场景(如三态:缺失 / null / 有值),应优先用
database/sql.NullString或niljson.NilString,它们内部已封装好判断逻辑
最易被忽略的一点:null 和字段缺失在反射层面完全不可区分
对于 type X struct { Name *string },以下三种 JSON 输入在反射视角下结果一致:{"name":"a"}、{"name":null}、{} —— 它们都导致 Name 是某个指针值或 nil,但你无法单靠 reflect.Value 知道它来自哪一种。
要真正区分,必须放弃纯反射路径:要么用 json.RawMessage 原样保留字节再判断 string(raw) == "null",要么用自定义类型实现 UnmarshalJSON 并在方法内记录来源状态。反射在这里只是执行者,不是决策者。


















