Go语言 之json使用
Go 语言中,官方提供了一个专门的包 encoding/json
json 反序列(解析)
假设有这样一个 json 数据,我要将其解析为 go 结构体
{ "name": "kuizuo", "age": 20 }
首先需要定义结构体,通常可以使用 json 转 go 结构体的在线工具,如下图
其转化代码如下
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
var p Person
jsonString := `{"name": "kuizuo", "age" : 20}`
err := json.Unmarshal([]byte(jsonString), &p)
if err == nil {
fmt.Println(p.Name)
fmt.Println(p.Age)
} else {
fmt.Println(err)
}
}
主要步骤
- 定义结构体(一般结构体的每个字段第一个字母大写),并新建结构体变量
- 核心代码
err := json.Unmarshal([]byte(jsonString), &p)
,如果解析有效那么 err 为 nil,并将 p 赋值其 json 数据。 - 访问结构体 p 的成员
解析 json 数组
如果要解析 json 数组,其步骤同上,演示代码如下
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
var persons []Person
jsonString := `[{"name": "kuizuo", "age" : 20},{"name": "愧怍", "age" : 22}]`
err := json.Unmarshal([]byte(jsonString), &persons)
if err == nil {
for _, p := range persons {
fmt.Print("\t\n", p.Name)
fmt.Print("\t", p.Age)
}
} else {
fmt.Println(err)
}
}
哪怕对于再复杂的 json 数据,都要先将 json 转为 go 结构体,然后执行json.Unmarshal
。
自定义属性名称的映射
假设 json 的 key 值存在空格(一般情况下不可能,以我多年读写 json 的经历都没看到过),由于 go 中无法将空格做为变量标识符(貌似没有语言支持空格当标识符),而 json-to-go 工具会将空格清楚,并将下个单词首字母大写。
{
"user name": "kuizuo"
}
type AutoGenerated struct {
UserName string `json:"user name"`
}
当然,这里的 UserName 可以随便命名 go 变量规范的名字,只要json:"user name"
不变,结构体映射的还是user name
属性。
map[string]interface
对于未知属性,并且不想定义结构体的话,go 有个很典型的解决方法是采用 map[string]interface{}
,在一些情况(仅一层 json 数据)下确实会比较方便,但也会存在 json 属性不存在导致读取错误的情况。
import (
"encoding/json"
"fmt"
)
func main() {
jsonString := `
{
"code": 200,
"data": {
"username": "kuizuo",
"age": 20
}
}`
var result map[string]interface{}
err := json.Unmarshal([]byte(jsonString), &result)
if err == nil {
fmt.Println(result["code"])
username := result["data"].(map[string]interface{})["username"]
fmt.Println(username)
} else {
fmt.Println(err)
}
}
在 Go1.18 更新中,any 作为一个新的关键字出现。any 本质上是 interface 的别名
type any = interface{}
map[string]interface{}
也可写为 map[string]any
json 序列化
既然前面是 json 转 go 结构体,那这肯定是将 go 结构体转为 json。
同样的,既然想要生成 json 数据,那么也可将要生成的 json 数据放到 json-to-go 里转为 go 结构体。如
{
"id": 1,
"username": "kuizuo",
"hobby": ["敲代码", "吃饭", "睡觉"]
}
type AutoGenerated struct {
ID int `json:"id"`
Username string `json:"username"`
Hobby []string `json:"hobby"`
}
对应的 go 代码如下
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Hobby []string `json:"hobby"`
}
func main() {
user := &User{
ID: 1,
Username: "kuizuo",
Hobby: []string{"敲代码", "吃饭", "睡觉"},
}
result, _ := json.Marshal(user)
fmt.Println(string(result))
}
主要步骤
- 定义结构体,并新建结构体指针,同时赋值
- 调用
result, _ := json.Marshal(user)
,此时的 result 为字节数组。 string(result)
将其转为 json 字符串
json 序列化没什么过多补充的,上面这里例子实际中已经足够使用了。
相关 json 库
tidwall/gjson: Get JSON values quickly - JSON parser for Go (github.com) 强烈推荐(还支持 jsonpath 语法)
总结
和 js 相比,go 对 json 的操作可以说是比较繁琐,但这并不是只有 go 这样,静态类型的语言都相对繁琐。在 go 是定义结构体,而在 java 则是定义类,每种语言都有数据格式的规范。不过 json 序列化与反序列化还是 js 最为方便,毕竟 json(JavaScript Object Notation)本身就作为 js 的对象数据表达形式。