初面网初面网

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 必须传指针,否则数据写不进去。

更新于 2026/8/13