disallowunknownfields 必须在首次 decode 前调用才生效,否则无效;它仅校验顶层结构体字段,不递归检查嵌套结构体,且对 map[string]interface{} 无效,go 1.10+ 才支持。

Decoder.DisallowUnknownFields 是 Go 标准库中防止 JSON 解码时忽略未知字段的开关,但它**只在调用 Decode 时生效,且必须在首次调用前设置**。设晚了、设错了、或没设,就完全不起作用。
为什么 DisallowUnknownFields 没报错?
最常见原因是:你调用了 json.NewDecoder,但没在第一次 Decode 前调用 DisallowUnknownFields() 方法。
这个方法返回的是 *json.Decoder 本身(链式调用),但它**不修改已存在的解码器状态**——它只是把一个内部标记设为 true。一旦 Decode 开始解析流,再调就无效。
- 错误写法:
dec := json.NewDecoder(r); dec.Decode(&v); dec.DisallowUnknownFields()→ 完全没用 - 正确写法:
dec := json.NewDecoder(r).DisallowUnknownFields(); dec.Decode(&v) - 如果用的是
bytes.Reader或strings.NewReader,也一样:必须链式调用或立即设置
DisallowUnknownFields 对嵌套结构体的影响
它默认只检查顶层结构体的字段,**不会递归检查嵌套结构体中的未知字段**,除非嵌套类型也显式启用了该行为。
例如:
type User struct {
Name string `json:"name"`
Info Info `json:"info"`
}
type Info struct {
Age int `json:"age"`
}
若 JSON 是 {"name":"a","info":{"age":25,"city":"bj"}},city 不会触发错误——因为 Info 类型没启用 DisallowUnknownFields。
- 要严格校验整个树,每个嵌套结构体对应的解码都得单独用带
DisallowUnknownFields的Decoder - 或者改用
json.Unmarshal+ 自定义UnmarshalJSON方法做字段白名单检查(更可控但更重) -
json.RawMessage字段会跳过解析,也不受此限制影响
和 json.Unmarshal 的区别与取舍
json.Unmarshal 是一次性内存解码,没有 DisallowUnknownFields 接口;它只认结构体 tag 和字段可见性。想实现类似效果,只能靠中间层预检或自定义 UnmarshalJSON。
-
Decoder适合流式读取(HTTP body、文件)、大 JSON 或需要复用解码器的场景 -
Unmarshal更轻量,但无法全局禁止未知字段——哪怕你给顶层结构体加了json:",string"之类 tag,也不会报未知字段错 - 如果你用的是
http.Request.Body,注意:body 只能读一次,DisallowUnknownFields失效后无法重试,建议尽早设置
真正容易被忽略的一点:这个功能只在 Go 1.10+ 生效,而且对 map 类型(如 map[string]interface{})完全无效——无论是否启用,未知字段都会被塞进 map。需要强约束时,别依赖它来兜底。











