go结构体json序列化默认用大驼峰字段名,需通过json tag显式指定小写下划线风格;空tag忽略字段,omitempty跳过零值,嵌套结构需手动标注,匿名字段不继承tag,自定义序列化用marshaljson方法。

Go 结构体 JSON 序列化默认用字段名,但实际需要下划线命名
Go 的 json.Marshal 默认使用结构体字段的 Go 名(即大驼峰),而多数 REST API 要求小写下划线风格(如 user_name)。这不是 bug,是设计使然——Go 通过 struct tag 显式控制序列化行为,避免隐式转换带来的歧义。
关键点:必须用 json tag 显式声明,否则字段名不会被改写;tag 值为空字符串(json:"")会导致该字段被忽略;tag 中带 omitempty 表示零值不输出。
示例:
type User struct {
ID int `json:"id"`
UserName string `json:"user_name"`
Email string `json:"email,omitempty"`
}
这样 json.Marshal(User{ID: 1, UserName: "alice"}) 输出 {"id":1,"user_name":"alice"},Email 因为空字符串且带 omitempty 被跳过。
嵌套结构体和匿名字段的 tag 处理容易漏掉
嵌套结构体不会自动继承外层 tag 规则,每个字段都需单独标注。匿名字段若未加 tag,则直接展开其内部字段,且仍按原名或其自身 tag 序列化。
常见错误:以为给匿名字段加个别名 tag 就能整体重命名,其实不行——JSON 不支持“对象别名”,只能靠字段级控制。
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
- 如果想把
Address嵌套结构整个映射为shipping_address字段,必须在父结构体中定义一个新字段,比如ShippingAddress Address `json:"shipping_address"` - 匿名字段
Address写成Address `json:"shipping_address"`是无效的:这只会让Address的字段(如City)直接平铺到顶层,且仍用各自 tag,不会包进shipping_address对象里 - 切片、map 类型字段同样要逐个打 tag,例如
Tags []string `json:"tags"`
零值字段不输出时,要注意 bool/int/float 的“零”含义不同
omitempty 对不同类型判断“是否为零值”的逻辑一致,但开发者常误判哪些值会被跳过:
-
bool:只有false被忽略;true总是输出 -
int/int64等:只有0被忽略 -
string:只有空字符串""被忽略 -
pointer或interface{}:nil 值被忽略 - 注意:
omitempty不影响json:"-"(完全忽略字段)的优先级,后者更高
典型陷阱:API 要求显式传 "is_active": false,但加了 omitempty 后这个字段就消失了。此时应去掉 omitempty,或改用指针类型(*bool)来区分“未设置”和“设为 false”。
自定义序列化逻辑要用 MarshalJSON 方法,但别滥用
当 tag 无法满足需求(比如字段值需动态计算、格式需特殊处理、或要隐藏敏感字段逻辑),就得实现 MarshalJSON() ([]byte, error) 方法。这是 Go 的标准接口,json.Marshal 会自动调用它。
但要注意:
- 方法内不能再调用
json.Marshal原结构体,否则会无限递归(除非你手动构造 map 或跳过该字段) - 推荐做法:构造一个临时匿名 struct 或 map,只包含要输出的字段,并用字面量赋值
- 性能上比纯 tag 方式略低,因为多了内存分配和反射开销;仅在必要时用
例如,想把时间字段转为秒级时间戳而非 RFC3339 字符串:
func (u User) MarshalJSON() ([]byte, error) {
type Alias User // 防止递归
return json.Marshal(&struct {
Timestamp int64 `json:"created_at"`
Alias
}{
Timestamp: u.CreatedAt.Unix(),
Alias: (Alias)(u),
})
}
最易被忽略的是:struct tag 的拼写错误(比如写成 josn)或空格(json: "name" 中冒号后多空格)会导致 tag 完全失效,回退到默认字段名。调试时先检查 tag 语法是否合法,比查逻辑更高效。










