JSON
JSON库(encoding/json)有两个互逆的操作:序列化和反序列化。
- 序列化,对象转JSON(Marshal)
- 反序列化,JSON转对象(Unmarshal)
结构体与JSON互转
字段必须大写开头才能被 json 包识别(否则不会序列化)。
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string
Age int
}
func main() {
u := User{Name: "runoob", Age: 18}
// 序列化:结构体转JSON
data, _ := json.Marshal(u)
fmt.Println(string(data)) // {"Name":"runoob","Age":18}
// 反序列化:JSON转结构体
jsonStr := `{"Name":"go","Age":20}`
var u2 User
json.Unmarshal([]byte(jsonStr), &u2)
fmt.Println(u2.Name, u2.Age)
}
结构体标签(tag)控制字段名
默认输出字段名就是结构体字段名(大写开头)。用 json:"xxx" 标签可以指定输出的 JSON 字段名:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"` // 序列化为 name
Age int `json:"age"`
Email string `json:"email,omitempty"` // omitempty: 空值时不输出该字段
Phone string `json:"phone,omitempty"`
}
func main() {
u := User{Name: "runoob", Age: 18}
data, _ := json.Marshal(u)
fmt.Println(string(data)) // {"name":"runoob","age":18}
}
omitempty 用于值为空时不输出该字段(0、空字符串、nil 都算空)。
忽略字段
字段标签写成 json:"-" 时,该字段完全不参与序列化和反序列化。
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
secret string // 小写字段,json包自动忽略
Pass string `json:"-"`
}
func main() {
u := User{Name: "runoob", Age: 18, Pass: "123456"}
data, _ := json.Marshal(u)
fmt.Println(string(data)) // {"name":"runoob","age":18}
}
map 与 JSON 互转
不确定字段结构时,可以直接用 map 接收任意 JSON 数据:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// map 转 JSON
m := map[string]any{
"name": "runoob",
"age": 18,
}
data, _ := json.Marshal(m)
fmt.Println(string(data)) // {"age":18,"name":"runoob"}
// JSON 转 map
jsonStr := `{"name":"go","age":20,"tags":["a","b"]}`
var m2 map[string]any
json.Unmarshal([]byte(jsonStr), &m2)
fmt.Println(m2["name"], m2["age"], m2["tags"])
}
格式化输出
json.MarshalIndent 输出带缩进的 JSON,方便查看和调试:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
u := User{Name: "runoob", Age: 18}
data, _ := json.MarshalIndent(u, "", " ")
fmt.Println(string(data))
}
输出:
{
"name": "runoob",
"age": 18
}
流式读写
JSON 数据量较大时,用 json.NewEncoder / json.NewDecoder 直接对流读写,避免整串加载内存。
package main
import (
"encoding/json"
"fmt"
"strings"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
// 从一个 Reader 流式读取 JSON
jsonStr := `{"name":"go","age":20}`
var u User
json.NewDecoder(strings.NewReader(jsonStr)).Decode(&u)
fmt.Println(u.Name, u.Age)
// 编码器,多用于向 http.ResponseWriter 直接写 JSON
var buf strings.Builder
json.NewEncoder(&buf).Encode(u)
fmt.Println(buf.String())
}
反序列化时字段匹配是大小写不敏感的(name 能匹配 Name),且多余的 JSON 字段会被丢弃。Unmarshal 必须传指针,否则数据写不进去。