1. 什么是 Json 序列化
在计算机科学中,序列化是将数据结构或对象转换为可存储或传输格式的过程。反序列化则是将已序列化的数据还原回其原始格式的过程。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,通常用于客户端和服务器之间的数据传输。Go语言中的标准库中提供了对 JSON 的编码解码支持,但是有时候我们需要自定义 JSON 的序列化规则,以满足我们的特定需求。
2. Go 自定义 Json 序列化规则
2.1 自定义字段的 JSON 名称
在 Go 中,可以通过在字段或方法上加上 `json:""` 标签的方式来自定义 JSON 的名称。例如,我们有如下定义:
type User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
这样,在进行 JSON 编码的时候,将会使用 `first_name` 和 `last_name` 来作为 JSON 对象的属性名:
u := User{FirstName: "John", LastName: "Doe"}
jsonData, err := json.Marshal(u)
fmt.Println(string(jsonData)) // {"first_name":"John","last_name":"Doe"}
2.2 在序列化时忽略字段
有时候,我们希望某些字段在进行 JSON 编码时被忽略掉,这时可以在该字段上加上 `json:"-"` 标签。例如:
type User2 struct {
FirstName string `json:"first_name"`
LastName string `json:"-"`
}
这样,在进行 JSON 编码的时候,将会忽略 `last_name` 字段,生成的 JSON 对象中只包含 `first_name` 字段:
u2 := User2{FirstName: "John", LastName: "Doe"}
jsonData, err := json.Marshal(u2)
fmt.Println(string(jsonData)) // {"first_name":"John"}
2.3 在序列化时添加额外字段
有时候,我们需要在进行 JSON 编码时添加一些额外的字段,这时可以定义一个自定义类型,然后在该类型上定义一个 `MarshalJSON` 方法以实现定制的 JSON 序列化。例如:
type User3 struct {
FirstName string
LastName string
}
type User3JSON struct {
*User3
FullName string `json:"full_name"`
}
func (u *User3) MarshalJSON() ([]byte, error) {
return json.Marshal(&User3JSON{
User3: u,
FullName: u.FirstName + " " + u.LastName,
})
}
这样,在进行 JSON 编码时会调用 `MarshalJSON` 方法,生成的 JSON 对象将会包含 `full_name` 字段:
u3 := User3{FirstName: "John", LastName: "Doe"}
jsonData, err := json.Marshal(u3)
fmt.Println(string(jsonData)) // {"full_name":"John Doe","FirstName":"John","LastName":"Doe"}
2.4 在序列化时处理时间
在进行 JSON 序列化时,需要特别注意处理时间类型的字段。在 Go 中,时间类型的字段默认使用 RFC3339 格式进行序列化。例如:
type Post struct {
Title string `json:"title"`
Body string `json:"body"`
CreatedAt time.Time `json:"created_at"`
}
post := Post{
Title: "Hello, World!",
Body: "This is my first post.",
CreatedAt: time.Now(),
}
jsonData, err := json.Marshal(post)
fmt.Println(string(jsonData))
输出结果如下:
{
"title": "Hello, World!",
"body": "This is my first post.",
"created_at": "2022-03-01T14:23:33.93839+08:00"
}
可以看到,时间类型的字段被序列化成了字符串。但是,有时候我们需要将时间序列化成指定的格式,或者进行时区转换等操作。这时,可以使用 `Marshaler` 和 `Unmarshaler` 接口来实现自定义序列化和反序列化逻辑。
例如,我们希望在进行 JSON 序列化时,将时间格式化为指定的字符串格式,可以定义如下类型:
type Post2 struct {
Title string `json:"title"`
Body string `json:"body"`
CreatedAt time.Time `json:"created_at"`
}
func (p *Post2) MarshalJSON() ([]byte, error) {
type Alias Post2
return json.Marshal(&struct {
*Alias
CreatedAt string `json:"created_at"`
}{
Alias: (*Alias)(p),
CreatedAt: p.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
这里定义了一个 `MarshalJSON` 方法,使用 `type Alias` 来避免递归。在方法中定义了一个新的结构体,并在其中包含了 `Post2` 类型的所有字段,并额外添加了一个 `CreatedAt` 字段,用于存储格式化后的时间字符串。在最后,再调用 `json.Marshal` 对新的结构体进行序列化。这样,在进行 JSON 序列化时,就会调用 `MarshalJSON` 方法,将时间类型序列化成我们指定的格式:
post2 := Post2{
Title: "Hello, World!",
Body: "This is my first post.",
CreatedAt: time.Now(),
}
jsonData, err := json.Marshal(post2)
fmt.Println(string(jsonData))
输出结果如下:
{
"title": "Hello, World!",
"body": "This is my first post.",
"created_at": "2022-03-01 14:23:33"
}
2.5 总结
本文介绍了 Go 中自定义 JSON 序列化规则的方法,包括自定义字段的 JSON 名称、在序列化时忽略字段、在序列化时添加额外字段和在序列化时处理时间。对于需要定制 JSON 序列化的应用场景,这些方法可以帮助我们更灵活地处理数据。