Go struct 标签

结构体字段上的元数据字符串;encoding/json 等通过反射读取,驱动序列化、校验与 ORM 映射。

#type / concept #status / growing #tech / dev #resource / go

[!info] 关联笔记

Go struct 标签

这个概念为什么会出现

结构体字段在 Go 里首先是内存布局与类型信息。但工程还需要不污染领域类型的元数据

  • JSON 字段名与 omitempty
  • DB 列名
  • 校验规则
  • 配置库的 key

若为每种格式再写一套平行结构体,重复且易漂。struct tag 把约定字符串贴在字段旁,由库在运行时用反射读取。语言只规定标签是字符串与惯用 key:"value" 形式;语义由各库定义。

[!abstract] 一句话理解 struct tag 是字段声明上的只读字符串元数据;reflect.StructTag 按 key 解析;encoding/json 等标准库与生态库据此决定编解码与映射行为。

最小可运行示例

先把示例放进业务场景,再看代码:

场景:用户 API 响应 DTO,密钥绝不能出站

GET /me 返回用户资料 JSON:

  • 字段名要是前端约定的 id/name(不是 Go 的 ID/Name
  • 邮箱为空时不要输出 "email":""
  • 内存里有 Token 用于服务端会话,序列化时必须丢掉

struct tag 就是贴在字段上的元数据,给 encoding/json 等库通过反射读取。

package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

// UserDTO:对外 JSON 形状由 tag 描述,而不是字段名本身。
type UserDTO struct {
	ID    int64  `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email,omitempty"` // 空则省略
	Token string `json:"-"`               // 永不输出
}

func main() {
	u := UserDTO{ID: 1, Name: "Ada", Token: "secret"}
	// Email 为零值 "" → omitempty 生效,JSON 里没有 email
	// Token 有值,但 json:"-" 仍被忽略
	b, err := json.Marshal(u)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b)) // {"id":1,"name":"Ada"}

	// 调试/写通用库时:用反射读出 tag 原文
	t := reflect.TypeOf(u)
	f, _ := t.FieldByName("Email")
	fmt.Println("email tag:", f.Tag.Get("json")) // email,omitempty
}

建议运行:

go run .

期望输出:

{"id":1,"name":"Ada"}
email tag: email,omitempty

结合场景再看三个关注点

  1. tag 是给库看的元数据,语言本身不解释 json 含义。
  2. json:"-" 防泄漏;密钥、内部 token 默认应忽略。
  3. omitempty 只影响编码是否输出零值;标签必须用反引号字符串。

核心概念与准确模型

语法

Field Type `key:"value" other:"x,y"`
  • 反引号原始字符串
  • 多个 key:"value" 以空格分隔
  • 值里可用逗号表达选项(由具体库解释

Spec — Tag 规定 tag 是可选字符串字面量;解析惯例见 reflect.StructTag

反射 API

tag := field.Tag            // reflect.StructTag
v := tag.Get("json")        // 无则 ""
v, ok := tag.Lookup("json") // 区分缺失与空值

Get 对格式错误可能返回空;生产库应健壮解析。

标准库:json 标签(最常用)

文档:encoding/json

片段含义
json:"name"字段名
json:"-"忽略
json:",omitempty"空值省略(名仍默认)
json:"name,omitempty"改名 + 省略
json:",string"部分数值等按 JSON 字符串编解码

“空值”对 bool/数字/字符串/指针/切片等定义不同,见文档。

可见性

只有导出字段参与 encoding/json 默认编解码。标签不能让小写字段被 json 包导出。

嵌入字段

嵌入 struct 的字段会“提升”;标签与冲突规则由具体编解码器处理。匿名字段本身也可带 tag。

其它常见 key(生态)

key典型用途
xmlencoding/xml
yamlyaml 库
db / gormSQL 映射
validate校验器
mapstructuremap→struct
form / queryHTTP 绑定

不要假设所有 key 语义统一;以对应库文档为准。

性能与设计边界

每次反射读 tag 有成本;热路径库会缓存类型描述。领域核心逻辑不应依赖 tag 才能正确——tag 是适配层配置。

边界情况

  1. 标签拼写错误:静默使用默认字段名,难发现。
  2. omitempty 与 false/0:合法零值被省略导致 API 歧义。
  3. 指针 vs 值:用 *T 区分“未设置”与零值。
  4. 冲突字段名:多字段映射同一 JSON 名行为依赖库。
  5. 自定义类型:需 MarshalJSON/UnmarshalJSON 时 tag 不够用。
  6. 安全json:"-" 防敏感字段泄漏;测试覆盖序列化快照。

常见误区

[!warning] 常见误区:tag 是语言强制模式 错误:以为编译器理解 validate:"required"
正确:只是字符串;无库读取则无效果。

[!warning] 常见误区:给未导出字段加 json tag 期望生效 错误:name string \json:“name”“。
正确:字段名首字母大写,或手写编解码。

[!warning] 常见误区:用 tag 堆砌全部业务规则 错误:几十个 validate 规则取代领域校验。
正确:边界用 tag/校验库;核心不变量用代码。

[!warning] 常见误区:手写解析不遵循 StructTag 格式 错误:自创无法用 Get 的格式。
正确:遵循 key:"value" 空格分隔惯例。

工程实践

  1. API DTO 与领域模型分离,tag 留在 DTO。
  2. 敏感字段默认 json:"-" 或显式 DTO
  3. 契约测试:黄金 JSON 文件防 tag 误改。
  4. 生成:protobuf/openapi 生成 struct 时审 tag。
  5. 统一 key 字典,避免 json/Json 混用。
  6. 自定义 tag 写清文档与 Lookup 错误处理。
  7. 与嵌入:明确提升字段的 JSON 形状。
// 可选字段常用指针
type Patch struct {
	Name *string `json:"name,omitempty"`
}

可验证实验

实验 1:omitempty

比较 Email:"" 与有值时的 JSON。

实验 2:json:"-"

确认 Token 不出现。

实验 3:反射 Lookup

对缺失 key 与 key:"" 区分 ok

实验 4:未导出字段

小写字段带 tag,观察是否出现在 JSON。

实验 5:自定义 MarshalJSON

与 tag 共存时谁优先(方法优先于默认 tag 逻辑)。

本节总结

  • 本质:字段级字符串元数据 + 反射消费。
  • 关键规则:导出字段、库定义语义、Get/Lookup、json 选项。
  • 最易错:拼写静默失败、omitempty 零值、未导出字段、安全泄漏。
  • 下一步反射 原理;校验 实践。

自测题

概念题

  1. 谁解释 validate:"required" 的含义?
  2. json:"-" 做什么?
  3. 为什么 tag 必须是字符串字面量?

代码推理题

type T struct {
	N int `json:"n,omitempty"`
}
json.Marshal(T{})

输出是什么?为何?

工程思考题

同一结构体既要 JSON 对外又要 SQL 扫描,如何组织 tag 与类型?

参考答案

展开
  1. 校验库在运行时,不是 Go 编译器。
  2. 编解码时忽略该字段。
  3. 语法上 tag 是可选 string lit;编译期固定进类型信息。
    代码题:{}——N 为零值且 omitempty 省略。
    工程题:可一结构多 tag;或 DTO/DAO 分离避免 json 与 db 耦合;扫描用 sql.Null* 或指针表达 NULL。

延伸阅读与资料来源

资料类型支撑
Spec — Tag规范语法
reflect.StructTag标准库解析
encoding/json标准库json tag 语义
encoding/xml标准库xml tag
Effective Go — Structs文档结构体风格

笔记元信息

  • 建议文件名:go-struct-tags.md
  • 所属阶段:类型与互操作
  • 建议下一篇:Go 反射
  • 本篇状态:已深化
创建于 2026/6/25 更新于 2026/7/15