《Go 程序设计语言》4.5 JSON — 结构体成员 Tag 1. Tag 是什么? Tag(标签) 是附加在结构体字段上的元信息字符串 ,用反引号 ` 包裹:
1 2 3 4 5 6 type Movie struct { Title string Year int `json:"released"` Color bool `json:"color,omitempty"` Actors []string }
💡 Tag 不影响 Go 代码的逻辑 ,它是给 encoding/json 等包读取的”标注”,用来控制序列化/反序列化 的行为。
2. Tag 的语法 1 `key:"value" key2:"value2"`
以 JSON 为例,通用格式为:
写法
含义
json:"released"
JSON 字段名为 released
json:"color,omitempty"
字段名 color,零值时省略
json:"-"
完全忽略 该字段,不参与 JSON 编解码
json:",omitempty"
字段名用默认(Go 字段名),零值时省略
3. 实际效果演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type Movie struct { Title string Year int `json:"released"` Color bool `json:"color,omitempty"` Actors []string } m := Movie{ Title: "Casablanca" , Year: 1942 , Color: false , Actors: []string {"Humphrey Bogart" , "Ingrid Bergman" }, } data, _ := json.MarshalIndent(m, "" , " " ) fmt.Println(string (data))
输出结果:
1 2 3 4 5 { "Title" : "Casablanca" , "released" : 1942 , "Actors" : [ "Humphrey Bogart" , "Ingrid Bergman" ] }
🔍 关键字段分析
Go 字段
Tag
JSON 输出
原因
Title
无
"Title"
无 Tag,直接用 Go 字段名
Year
json:"released"
"released"
Tag 指定了映射名称
Color
json:"color,omitempty"
❌ 未出现
值为 false(零值)+ omitempty → 被省略
Actors
无
"Actors"
非零值,正常输出
4. omitempty 的零值规则 omitempty 选项会在字段为零值 时省略该字段:
类型
零值
是否被省略
bool
false
✅ 是
int / float
0 / 0.0
✅ 是
string
""
✅ 是
slice / map / pointer
nil
✅ 是
struct
所有字段为零值
✅ 是
⚠️ 注意:空切片 []int{} 不是 nil,不会 被 omitempty 省略!
1 2 3 4 5 6 7 8 9 type Data struct { Items []int `json:"items,omitempty"` } d1 := Data{Items: []int {}} d2 := Data{Items: nil }
5. 解码时 Tag 同样生效 1 2 3 4 5 6 jsonStr := `{"released": 1942, "Title": "Casablanca"}` var m Moviejson.Unmarshal([]byte (jsonStr), &m) fmt.Println(m.Year)
🔹 解码时的匹配规则
精确匹配 Tag (优先级最高):json:"released" → 匹配 Year
大小写不敏感匹配字段名 :"title" → 匹配 Title
完全匹配字段名 :"Title" → 匹配 Title
💡 建议始终使用 Tag 明确指定映射关系,避免大小写歧义。
6. 选择性解码 可以定义只包含部分字段 的结构体,只解析关心的字段:
1 2 3 4 5 6 7 8 9 type MovieTitle struct { Title string Year int `json:"released"` } var mt MovieTitlejson.Unmarshal(data, &mt)
💡 这是处理大型 JSON 响应时的常用技巧,可提升性能并简化代码。
7. Tag 是通用机制,不只用于 JSON Tag 是 Go 的通用元数据机制 ,不同的包读取不同的 key:
1 2 3 4 type User struct { Name string `json:"name" xml:"name" db:"user_name"` Email string `json:"email,omitempty" validate:"required,email"` }
Tag Key
使用者
用途
json
encoding/json
JSON 序列化/反序列化
xml
encoding/xml
XML 编解码
db / gorm
数据库 ORM(sqlx、GORM)
映射数据库列名
validate
校验库(go-playground/validator)
字段校验规则
yaml
YAML 解析库
YAML 编解码
form
Web 框架(Gin、Echo)
表单参数绑定
💡 一个字段可以同时拥有多个 Tag,服务于不同场景。
8. 只有导出字段参与编解码 1 2 3 4 type User struct { Name string age int }
字段名
是否导出
是否参与 JSON 编解码
Name
✅ 是
✅ 是
age
❌ 否
❌ 否(即使有 Tag 也无效)
⚠️ 重要 :encoding/json 等标准库只能访问导出字段 (大写开头),私有字段会被静默忽略。
🔑 关键要点速记
要点
说明
本质
结构体字段的元信息字符串,用反引号包裹
作用
控制 JSON 字段名映射、零值省略等行为
json:"name"
指定 JSON 字段名
omitempty
零值时不输出该字段
json:"-"
完全忽略该字段
解码匹配
Tag 优先级最高,其次大小写不敏感匹配字段名
导出要求
只有大写开头 的字段才参与 JSON 编解码
通用机制
不只用于 JSON,xml、db、yaml、validate 等包都用 Tag
🎯 实践建议 ✅ 始终为公共 API 结构体添加 Tag 1 2 3 4 5 6 7 8 9 10 11 type User struct { UserName string EmailAddr string } type User struct { UserName string `json:"username"` EmailAddr string `json:"email"` }
✅ 合理使用 omitempty 减少冗余 1 2 3 4 5 6 type APIResponse struct { Code int `json:"code"` Message string `json:"message,omitempty"` Data interface {} `json:"data,omitempty"` Error string `json:"error,omitempty"` }
✅ 忽略敏感字段 1 2 3 4 5 type User struct { ID uint `json:"id"` Name string `json:"name"` Password string `json:"-"` }
✅ 数据库列名映射 1 2 3 4 5 type User struct { ID uint `json:"id" db:"id"` Name string `json:"name" db:"user_name"` Email string `json:"email" db:"email_addr"` }
✅ 校验规则集成 1 2 3 4 5 type RegisterReq struct { Username string `json:"username" validate:"required,min=3,max=20"` Email string `json:"email" validate:"required,email"` Password string `json:"password" validate:"required,min=8" json:"-"` }
🔧 调试技巧 查看结构体的实际 Tag 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import ( "fmt" "reflect" ) func printTags (v interface {}) { t := reflect.TypeOf(v) if t.Kind() == reflect.Ptr { t = t.Elem() } for i := 0 ; i < t.NumField(); i++ { field := t.Field(i) fmt.Printf("%s: %s\n" , field.Name, field.Tag) } }
验证 JSON 映射 1 2 3 4 5 6 7 8 9 10 11 12 13 func testJSON (v interface {}) { data, err := json.Marshal(v) if err != nil { log.Fatal(err) } fmt.Println(string (data)) var decoded interface {} json.Unmarshal(data, &decoded) fmt.Printf("Decoded: %+v\n" , decoded) }
📌 一句话总结 :结构体 Tag 是 Go 中连接代码与外部数据格式的”桥梁”,理解其语法、零值规则和导出限制,是编写健壮 API 和数据交互层的关键。