首页 / Go 语言入门教程 / encoding/json

Go 语言入门教程

encoding/json

本教程共 80 篇 · 第 75 篇 · 更新于 2026-07-27 · 约 8 分钟阅读

GoGo 入门教程jsonMarshalUnmarshal结构体标签序列化

75. encoding/json

本节目标:学会用 Marshal/Unmarshal 编解码 JSON,掌握结构体标签,了解流式编解码和自定义序列化。

序列化 Marshal

json.Marshal 把 Go 值转成 JSON 字节:

type User struct {
	Name string
	Age  int
}

u := User{Name: "Tom", Age: 18}
data, err := json.Marshal(u)
fmt.Println(string(data)) // {"Name":"Tom","Age":18}

想输出更易读的格式,用 MarshalIndent

data, _ := json.MarshalIndent(u, "", "  ")
fmt.Println(string(data))

输出:

{
  "Name": "Tom",
  "Age": 18
}

反序列化 Unmarshal

json.Unmarshal 把 JSON 解析回 Go 值:

jsonStr := `{"Name":"Tom","Age":18}`
var u User
err := json.Unmarshal([]byte(jsonStr), &u)
if err != nil {
	fmt.Println("解析失败:", err)
}
fmt.Println(u) // {Tom 18}

注意传的是 &u(指针),Unmarshal 需要修改原变量。

结构体标签

默认 JSON 的 key 就是字段名(大写开头)。实际开发里我们需要小写或改名的 key,用标签控制:

type User struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Email string `json:"email,omitempty"` // 空值省略
Addr  string `json:"-"`                 // 忽略这个字段
}

效果:

u := User{Name: "Tom", Age: 18}
data, _ := json.Marshal(u)
// {"name":"Tom","age":18}

常用标签:

标签作用
json:"name"key 改成 name
json:",omitempty"零值时不输出
json:"-"完全忽略
json:"name,omitempty"改名且空值省略
Tip

omitempty 对零值生效:字符串空串、数字 0、bool false、指针 nil、切片/map 为 nil。如果想保留 0 值输出,用指针类型。

字段类型对应

Go 类型JSON 类型
stringstring
int/float64number
boolboolean
[]Tarray
map[string]Tobject
*T(指针)对应类型或 null
any / interface{}任意类型

解析不确定结构

JSON 结构不确定时,先解析到 map[string]any

var result map[string]any
json.Unmarshal([]byte(`{"name":"Tom","tags":["a","b"],"score":95.5}`), &result)

fmt.Println(result["name"])  // Tom
fmt.Println(result["tags"])  // [a b]
fmt.Println(result["score"]) // 95.5
Warning

map[string]any 里的数字都是 float64 类型。要 int 得自己转:int(result["score"].(float64))。不确定结构最好还是定义结构体,类型更安全。

流式编码 Encoder

大数据量时用 json.Encoder 流式写入,不用一次占满内存:

file, _ := os.Create("users.json")
defer file.Close()

encoder := json.NewEncoder(file)

users := []User{
	{Name: "Tom", Age: 18},
	{Name: "Jerry", Age: 20},
}

for _, u := range users {
	encoder.Encode(u) // 每次写一个对象,自动换行
}

流式解码 Decoder

file, _ := os.Open("users.json")
defer file.Close()

decoder := json.NewDecoder(file)

for {
	var u User
	err := decoder.Decode(&u)
	if err == io.EOF {
		break
	}
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(u)
}
Note

Decoder 适合处理 JSON 数组流或多个连续 JSON 对象。HTTP 请求 body 解析也常用 json.NewDecoder(req.Body).Decode(&v),省内存。

自定义 MarshalJSON

实现 json.Marshaler 接口可以自定义序列化逻辑:

type Color struct {
	R, G, B uint8
}

func (c Color) MarshalJSON() ([]byte, error) {
	hex := fmt.Sprintf("\"#%02X%02X%02X\"", c.R, c.G, c.B)
	return []byte(hex), nil
}

func (c *Color) UnmarshalJSON(data []byte) error {
	// data 形如 "#FF8800"
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	fmt.Sscanf(s, "#%02X%02X%02X", &c.R, &c.G, &c.B)
	return nil
}

效果:

c := Color{255, 136, 0}
data, _ := json.Marshal(c)
fmt.Println(string(data)) // "#FF8800"

Go 1.26 新特性

Go 1.26 的 new(expr) 对 JSON 可选字段很实用。比如 age 字段用 *int,有值时 new(年龄) 直接生成指针:

type Person struct {
	Name string `json:"name"`
	Age  *int   `json:"age"` // 指针,nil 表示未知
}

func personJSON(name string, age int) ([]byte, error) {
	return json.Marshal(Person{
		Name: name,
		Age:  new(age), // 直接把 age 的值传给 new
	})
}

以前要写成 Age: &[]int{age}[0],现在清爽多了。

小结

  • Marshal 序列化,Unmarshal 反序列化,注意传指针
  • 结构体标签 json:"name,omitempty" 控制 key 名和空值省略
  • json:"-" 忽略字段
  • 不确定结构用 map[string]any,数字是 float64
  • Encoder/Decoder 流式编解码,省内存
  • 自定义 MarshalJSON/UnmarshalJSON 控制序列化行为
  • Go 1.26 new(expr) 方便生成可选字段指针

下一节讲 net/http 基础。