Go 结构体与 JSON 之间的转换

时间:2022-12-13 11:00:59

耐心和持久胜过激烈和*。

哈喽大家好,我是陈明勇,今天分享的内容是 Go 结构体与 JSON 之间的转换。如果本文对你有帮助,不妨点个赞,如果你是 Go 语言初学者,不妨点个关注,一起成长一起进步,如果本文有错误的地方,欢迎指出!

前言

在日常开发中,我们往往会将 ​​JSON​​​ 解析成对应的结构体,反之也会将结构体或 ​​Map​​​ 数据转成 ​​JSON​​​。接下来本文会通过 ​​JSON​​​ 包的两个函数,来介绍 ​​JSON​​​ 与 结构体之间的转换。

结构体转 JSON

​Marshal(v any) ([]byte, error)​​​:将 ​​v​​​ 转成 ​​JSON​​​ 数据,以 ​​[]byte​​ 的形式返回。

import (
"encoding/json"
"fmt"
)

type User struct {
Name string
Age int
Height float64
Weight *float64
Child bool
marriageStatus string
}

func main() {
weight := 120.5
user := User{
Name: "gopher",
Age: 18,
Height: 180.5,
Weight: &weight,
Child: false,
marriageStatus: "未婚",
}
jsonBytes, err := json.Marshal(user)
if err != nil {
fmt.Println("error: ", err)
return
}
fmt.Println(string(jsonBytes)) // {"Name":"gopher","Age":18,"Height":180.5,"Weight":120.5,"Child":false}
}

执行结果: ​​{"Name":"gopher","Age":18,"Height":180.5,"Weight":120.5,"Child":false}​

根据结果可知:

  • 不可导出字段(字段名以小写字母开头),是不能被转成 ​​JSON​​​ 的 ​​key​​​ 的,这也符合 ​​Go​​​ 的语法规定,以小写字母开头的变量或结构体字段等,不能在包外被访问。
  • 转换后的字段名,与结构体字段的名字一样。
  • 如果字段是指针类型,转换后的值为指针指向的字段值。

如果我们想要指定字段转换之后的命名,或者将一个可导出的字段进行忽略,可以通过给结构体字段打上 ​​JSON​​ 标签的方式实现。

import (
"encoding/json"
"fmt"
)

type User struct {
Name string `json:"name"`
Age int `json:"age"`
Height float64 `json:"height"`
Weight *float64 `json:"weight"`
Child bool `json:"child"`
MarriageStatus string `json:"-"`
}

func main() {
weight := 120.5
user := User{
Name: "gopher",
Age: 18,
Height: 180.5,
Weight: &weight,
Child: false,
MarriageStatus: "未婚",
}
jsonBytes, err := json.Marshal(user)
if err != nil {
fmt.Println("error: ", err)
return
}
fmt.Println(string(jsonBytes)) // {"name":"gopher","age":18,"height":180.5,"weight":120.5,"child":false}
}

通过给结构体字段定打上 ​​JSON​​​ 标签,可指定转成 ​​JSON​​​ 后的字段名,如果 ​​JSON​​​ 标签的值为 ​​-​​​,则在转换 ​​JSON​​ 时忽略此字段。

JSON 解析结构体

​Unmarshal(data []byte, v any) error​​​:将 ​​JSON​​ 解析成指定的结构体。

import (
"encoding/json"
"fmt"
)

type User struct {
Name string `json:"name"`
Age int `json:"age"`
Height float64 `json:"height"`
Child bool `json:"-"`
marriageStatus string
}

func main() {
userStr := `
{
"name": "gopher",
"age": 18,
"height": 180.5,
"child": true,
"marriageStatus": "未婚"
}
`
user := &User{}
err := json.Unmarshal([]byte(userStr), &user)
if err != nil {
fmt.Println("error: ", err)
return
}
fmt.Printf("%#v", user) // &main.User{Name:"gopher", Age:18, Height:180.5, Child:false, marriageStatus:""}
}

执行结果:​​&main.User{Name:"gopher", Age:18, Height:180.5, Child:false, marriageStatus:""}​

根据结果可知:

  • 使用​​Unmarshal​​函数时,我们需要传入结构体的指针类型,否则结构体字段的值将不会被改变,因为底层是通过指针去修改结构体字段的值。
  • ​JSON​​​ 解析时,​​JSON​​​ 的 ​​key​​ 与结构体字段的匹配规则是:
  • 优先查找 ​​JSON​​​ 标签值和 ​​key​​​ 一样的,找到则将 ​​value​​ 赋值给对应字段。
  • 如果没有 ​​JSON​​​ 标签值与 ​​key​​ 相匹配,则根据字段名进行匹配。
  • 可以发现,如果结构体字段是非导出字段或 ​​JSON​​​ 标签的值为 ​​-​​,将不会被匹配到。

小结

本文介绍了 ​​Go​​​ 语言里,​​JSON​​​ 与结构体之间的转换。在结构体转 ​​JSON​​​ 时,我们可以通过给字段打标签,指定转换后的 ​​key​​​ 命名,需要注意的是,如果结构体的字段为非导出字段或字段的 ​​JSON​​​ 标签值为 ​​-​​​,在转换 ​​JSON​​​ 时,将会被忽略。反之 ​​JSON​​ 解析结构体时也是一样的。