
本文详解如何通过结构体标签控制 Go 中 encoding/json 的字段序列化行为,避免敏感字段(如 Password)意外暴露在 JSON 输出中,重点介绍 json:"-" 和 omitempty 的正确用法及嵌入结构体场景下的注意事项。
本文详解如何通过结构体标签控制 go 中 `encoding/json` 的字段序列化行为,避免敏感字段(如 password)意外暴露在 json 输出中,重点介绍 `json:"-"` 和 `omitempty` 的正确用法及嵌入结构体场景下的注意事项。
在 Go 中,结构体字段默认会全部参与 JSON 序列化(只要字段是导出的),这可能导致敏感信息(如密码、令牌、内部状态等)被意外暴露。要精准控制字段是否输出,关键在于合理使用 encoding/json 包支持的结构体标签(struct tags)。
最常用且推荐的方式是为敏感字段显式指定 json:"-" 标签:
type User struct {
Name string `json:"name"`
Password string `json:"-"` // 完全排除该字段,无论值是否为空
Email string `json:"email"`
}此时调用 json.Marshal(&User{"Alice", "123456", "alice@example.com"}) 将输出:
{"name":"Alice","email":"alice@example.com"}Password 字段被彻底忽略,不会出现在 JSON 中。
⚠️ 注意:嵌入结构体(anonymous field)中的字段无法被外层结构体的同名字段标签覆盖。这是原问题的核心陷阱。例如:
type A struct {
Name string `json:"name"`
Password string `json:"password"` // 此处已导出并标记为 password
}
type B struct {
A
Password string `json:"-"` // ❌ 无效!它只隐藏 B.Password,但 A.Password 仍会被序列化
Locale string `json:"locale"`
}上述代码中,B.Password 的 json:"-" 并不能阻止 A.Password 被序列化——因为 A 是嵌入字段,其所有导出字段(包括 Password)会“提升”到 B 的字段集里,而 B.Password 是一个独立字段,与 A.Password 无覆盖关系。
✅ 正确解法有两种:
方案一(推荐):修改嵌入结构体 A 的标签
type A struct {
Name string `json:"name"`
Password string `json:"-"` // 直接在源头隐藏
}
type B struct {
A
Locale string `json:"locale"`
}方案二:避免嵌入,改用组合 + 手动映射(适合需精细控制的场景)
type B struct {
Name string `json:"name"`
Locale string `json:"locale"`
// Password 不声明,天然不输出
}补充说明:omitempty 标签常被误用为“隐藏敏感字段”的手段,但它仅在字段值为空(empty value)时跳过序列化(如 "", 0, nil, false)。对于密码这类必填非空字段,omitempty 完全无效——若设为 Password stringjson:"password,omitempty",只要Password非空字符串,它依然会输出。因此,对确定需隐藏的敏感字段,务必使用json:"-",而非依赖omitempty`。
总结:
- 敏感字段应统一使用 json:"-" 标签;
- 嵌入结构体的敏感字段必须在其原始定义处加 json:"-",外层无法覆盖;
- omitempty 适用于可选字段的条件省略,不适用于安全屏蔽;
- 生产环境建议结合 go vet 或静态分析工具(如 staticcheck)检查是否存在未标记的敏感字段。


















