12 序列化
2025/9/30大约 3 分钟
12 序列化
序列化的包比较多,最终的格式也有诸如 xml, json, protobuf 等类型,本节主要介绍在 RESTful API 中最常用的数据格式 —— json,Go 通过 encoding/json
包提供了结构体与 JSON 字符串之间的转换能力。
基本序列化
使用 json.Marshal()
将结构体转换为 JSON 字符串:
type Student struct {
ID int
Name string
Gender string
}
func main() {
s := Student{ID: 1, Gender: "男", Name: "张三"}
data, err := json.Marshal(s)
if err == nil {
fmt.Println(string(data))
}
// {"ID":1,"Name":"张三","Gender":"男"}
}
注意:只有 首字母大写的字段 才会被序列化,私有字段会被忽略。
基本反序列化
使用 json.Unmarshal()
将 JSON 字符串解析到结构体:
func main() {
jsonStr := `{"ID":1,"Name":"李四","Gender":"女"}`
var student Student
err := json.Unmarshal([]byte(jsonStr), &student)
if err != nil {
fmt.Println("解析失败:", err)
return
}
fmt.Printf("%+v\n", student)
}
理所当然的,反序列化时需要传入结构体指针,否则无法修改。
标签 Tag
前面我们说到了一个小写的字段是不会被序列化的,但是假如前端需要的字段就是小写的呢?此时只靠默认行为是不是就不行了,而解决这个问题的方案就是在定义结构体字段的时候加上一个 Tag,格式为:
`key:"value" key2:"value2"`
自定义字段名
type Student struct {
ID int `json:"id"`
Name string `json:"name"`
Gender string `json:"gender"`
}
此时序列化后的 JSON 字段名就会变为小写的 id
、name
、gender
。
常用 Tag 选项
type User struct {
Name string `json:"name"` // 指定字段名
Password string `json:"password,omitempty"` // 为空时忽略
Email string `json:"email,string"` // 永远都序列化为string
Age int `json:"-"` // 永远忽略该字段
}
注意:Tag 解析对格式要求极严格,key 与 value 之间 不能有空格,否则会解析失败且不报错。
嵌套结构体序列化
普通嵌套
type Address struct {
Province string `json:"province"`
City string `json:"city"`
}
type User struct {
Name string `json:"name"`
Address Address `json:"address"`
}
func main() {
u := User{
Name: "张三",
Address: Address{
Province: "广东",
City: "深圳",
},
}
data, _ := json.Marshal(u)
fmt.Println(string(data))
// {"name":"张三","address":{"province":"广东","city":"深圳"}}
}
匿名嵌套
匿名嵌套时,嵌套结构体的字段会被提升到外层:
type User struct {
Name string `json:"name"`
Address // 匿名嵌套
}
func main() {
u := User{
Name: "李四",
Address: Address{
Province: "北京",
City: "朝阳",
},
}
data, _ := json.Marshal(u)
fmt.Println(string(data))
// {"name":"李四","province":"北京","city":"朝阳"}
}
自定义序列化
实现 json.Marshaler
和 json.Unmarshaler
接口可以自定义序列化逻辑:
type Time time.Time
func (t Time) MarshalJSON() ([]byte, error) {
stamp := fmt.Sprintf(`"%s"`, time.Time(t).Format("2006-01-02"))
return []byte(stamp), nil
}
func (t *Time) UnmarshalJSON(data []byte) error {
str := string(data[1 : len(data)-1]) // 去掉引号
parsed, err := time.Parse("2006-01-02", str)
if err != nil {
return err
}
*t = Time(parsed)
return nil
}
然后直接包含这个字段进行序列化即可,就会按照我们设定的规则进行序列化和反序列化。