Struct Tag 与 JSON / Protobuf 序列化
第 14 章用反射读取过 struct tag。本章讲 tag 如何驱动序列化,以及 JSON 与 Protobuf 的差异。
1. JSON tag 的写法
type User struct {
Name string `json:"name"`
Age int `json:"age,omitempty"` // 零值时省略
Email string `json:"-"` // 永不序列化
}encoding/json 通过反射读 tag 决定字段名。
ℹ️omitempty 的坑
omitempty 在「零值」时省略:0、空串、nil、空切片都算。若业务上 0 是合法值,别加它,否则该字段会消失。
2. 自定义序列化
实现 MarshalJSON / UnmarshalJSON 可完全控制:
type Epoch int64
func (e Epoch) MarshalJSON() ([]byte, error) {
return []byte(strconv.FormatInt(int64(e), 10)), nil
}3. Protobuf:契约优先
Protobuf 用 .proto 定义契约,由工具生成 Go 代码,性能远胜 JSON。
syntax = "proto3";
message User {
string name = 1;
int32 age = 2;
}u := &pb.User{Name: "Alice", Age: 30}
data, _ := proto.Marshal(u) // 二进制,体积小、快💡JSON 还是 Protobuf
- 对外 HTTP API、配置文件:JSON 可读、易调试
- 服务间内部 RPC、高吞吐:Protobuf 省带宽、强类型
4. 两者的 tag 互不干涉
type Product struct {
ID int `json:"id" protobuf:"varint,1,opt,name=id"`
Name string `json:"name" protobuf:"bytes,2,opt,name=name"`
}反射读取时按 tag key 区分(Get("json") / Get("protobuf"))。
小结
jsontag 控制字段名;omitempty省零值,-跳过- 需要特殊规则就实现
MarshalJSON/UnmarshalJSON - Protobuf 契约优先、二进制编码,适合内部高吞吐
- JSON 适合对外可读场景,Protobuf 适合内部 RPC
- 不同格式的 tag 各自独立,靠反射的 key 区分