JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,广泛应用于前后端通信和配置文件。Go语言通过标准库encoding/json提供了强大的JSON序列化(编码)和反序列化(解码)支持。通过结构体标签(struct tags),可以灵活控制JSON字段名、空值处理等行为。
json.Marshal 将Go对象转为JSON;json.Unmarshal 将JSON转为Go对象。支持自定义时间格式、私有字段控制等。
定义结构体,并使用json.Marshal和json.Unmarshal进行转换。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
Email string
}
func main() {
// 序列化 Go对象 -> JSON
p := Person{Name: "Alice", Age: 30, Email: "alice@example.com"}
jsonData, err := json.Marshal(p)
if err != nil {
fmt.Println("序列化失败:", err)
return
}
fmt.Println(string(jsonData)) // {"Name":"Alice","Age":30,"Email":"alice@example.com"}
// 反序列化 JSON -> Go对象
var p2 Person
err = json.Unmarshal(jsonData, &p2)
if err != nil {
fmt.Println("反序列化失败:", err)
return
}
fmt.Printf("%+v\n", p2) // {Name:Alice Age:30 Email:alice@example.com}
}
通过反引号标签可以指定JSON字段名、是否忽略空值、字段忽略等。
type User struct {
ID int `json:"id"` // 重命名为 "id"
Name string `json:"name"` // 重命名为 "name"
Password string `json:"-"` // 忽略此字段(不序列化)
Email string `json:"email,omitempty"` // 空值时省略
CreatedAt string `json:"created_at,omitempty"`
}
func main() {
u := User{ID: 1, Name: "Bob", Password: "secret", Email: ""}
data, _ := json.Marshal(u)
fmt.Println(string(data)) // {"id":1,"name":"Bob"}
}
omitempty(零值省略)、string(将数字转换为字符串)、-,omitempty(忽略)。type Config struct {
Host string `json:"host"`
Port int `json:"port"`
}
cfg := Config{Host: "localhost", Port: 8080}
prettyJSON, _ := json.MarshalIndent(cfg, "", " ")
fmt.Println(string(prettyJSON))
// {
// "host": "localhost",
// "port": 8080
// }
当JSON结构不确定时,可以解析为map[string]interface{}或interface{}。
jsonStr := `{"name":"Alice","age":30,"hobbies":["reading","coding"]}`
var result map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &result)
if err != nil {
panic(err)
}
fmt.Println(result["name"]) // Alice
fmt.Println(result["hobbies"]) // [reading coding]
// 类型断言获取具体值
hobbies := result["hobbies"].([]interface{})
fmt.Println(hobbies[0]) // reading
interface{}时,数字默认是float64,注意类型断言。标准库默认将time.Time转换为RFC3339格式字符串。可以通过自定义类型实现自定义格式。
type CustomTime struct {
time.Time
}
func (ct CustomTime) MarshalJSON() ([]byte, error) {
return []byte(`"` + ct.Format("2006-01-02") + `"`), nil
}
func (ct *CustomTime) UnmarshalJSON(data []byte) error {
s := strings.Trim(string(data), `"`)
t, err := time.Parse("2006-01-02", s)
if err != nil {
return err
}
ct.Time = t
return nil
}
type Event struct {
Name string `json:"name"`
Date CustomTime `json:"date"`
}
func main() {
e := Event{Name: "Go Conference", Date: CustomTime{time.Now()}}
data, _ := json.Marshal(e)
fmt.Println(string(data)) // {"name":"Go Conference","date":"2025-06-08"}
}
默认情况下,如果JSON包含结构体没有的字段,Unmarshal会忽略。使用DisallowUnknownFields可以强制报错。
decoder := json.NewDecoder(strings.NewReader(`{"name":"Alice","age":30,"extra":true}`))
decoder.DisallowUnknownFields()
var u User
err := decoder.Decode(&u)
if err != nil {
fmt.Println(err) // 报错:json: unknown field "extra"
}
解析到interface{}时,大整数可能被解析为float64丢失精度。使用UseNumber保留原始数字字符串。
var data interface{}
decoder := json.NewDecoder(strings.NewReader(`{"id": 12345678901234567890}`))
decoder.UseNumber()
decoder.Decode(&data)
m := data.(map[string]interface{})
id := m["id"].(json.Number)
fmt.Println(id.String()) // 12345678901234567890
对于大文件或网络流,可以使用json.Decoder和json.Encoder逐条处理。
// 写入流
file, _ := os.Create("data.json")
enc := json.NewEncoder(file)
for _, item := range items {
enc.Encode(item)
}
// 读取流
dec := json.NewDecoder(file)
var item Item
for dec.More() {
dec.Decode(&item)
// 处理 item
}
package main
import (
"encoding/json"
"fmt"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data User `json:"data"`
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Role string `json:"role,omitempty"`
}
func main() {
jsonResp := `{
"code": 200,
"message": "success",
"data": {
"id": 1001,
"name": "John Doe"
}
}`
var resp Response
if err := json.Unmarshal([]byte(jsonResp), &resp); err != nil {
fmt.Println("解析失败:", err)
return
}
fmt.Printf("状态码: %d\n", resp.Code)
fmt.Printf("用户: %s (ID=%d)\n", resp.Data.Name, resp.Data.ID)
}
encoding/json包简洁而强大,通过结构体标签可以精细控制JSON的生成与解析。掌握上述技巧后,可以高效处理API对接、配置文件读写等常见任务。下一章我们将学习Go语言中的数据库操作(database/sql)。