Json
标准库 encoding/json 用两个函数包办 JSON:Marshal 把结构体编码成字节,Unmarshal 反过来解回结构体。这里整理基本用法、结构体标签、嵌套与动态 JSON,以及常见陷阱。
1. 基本用法
JSON 编码(序列化)
将 Go 变量转换为 JSON 格式的字符串。
go
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
person := Person{Name: "Alice", Age: 30}
// 将 person 序列化为 JSON 字符串
jsonData, err := json.Marshal(person)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(jsonData)) // 输出: {"Name":"Alice","Age":30}
}JSON 解码(反序列化)
将 JSON 格式的字符串转换为 Go 变量。
go
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
jsonString := `{"Name":"Alice","Age":30}`
var person Person
// 将 JSON 字符串反序列化为 person
err := json.Unmarshal([]byte(jsonString), &person)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(person) // 输出: {Alice 30}
}2. 结构体标签
在结构体字段上使用标签可以控制 JSON 编码和解码的行为。例如,可以指定 JSON 字段名、忽略某个字段、指定字段的解码方式等。
go
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"` // JSON 字段名为 "name"
Age int `json:"age"` // JSON 字段名为 "age"
Gender string `json:"gender,omitempty"` // 若为空则忽略该字段
}
func main() {
person := Person{Name: "Alice", Age: 30, Gender: ""}
jsonData, err := json.Marshal(person)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(jsonData)) // 输出: {"name":"Alice","age":30}
}3. 常见操作
处理嵌套结构体
可以处理包含嵌套结构体的 JSON 数据。
go
package main
import (
"encoding/json"
"fmt"
)
type Address struct {
City string
Country string
}
type Person struct {
Name string
Age int
Address Address
}
func main() {
jsonString := `{"Name":"Alice","Age":30,"Address":{"City":"New York","Country":"USA"}}`
var person Person
err := json.Unmarshal([]byte(jsonString), &person)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(person) // 输出: {Alice 30 {New York USA}}
}处理动态 JSON
可以使用 map[string]interface{} 来处理未知结构的 JSON 数据。
go
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonString := `{"Name":"Alice","Age":30,"Address":{"City":"New York","Country":"USA"}}`
var data map[string]interface{}
err := json.Unmarshal([]byte(jsonString), &data)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(data) // 输出: map[Name:Alice Age:30 Address:map[City:New York Country:USA]]
}4. 常见陷阱
大小写不匹配
JSON 字段名的大小写敏感,确保结构体标签与 JSON 字段名匹配。
go
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}使用空接口类型
使用 interface{} 可以处理任意类型的数据,但需要进行类型断言。
go
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonString := `{"Name":"Alice","Age":30}`
var data map[string]interface{}
json.Unmarshal([]byte(jsonString), &data)
name := data["Name"].(string)
age := data["Age"].(float64) // JSON 中的数字默认解析为 float64
fmt.Println(name, age)
}避免循环引用
Go 的 JSON 编码器不能处理包含循环引用的结构体。
5. 示例代码
下面是一个完整的示例代码,展示了 JSON 编码、解码和处理嵌套结构体:
go
package main
import (
"encoding/json"
"fmt"
)
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address Address `json:"address"`
}
func main() {
person := Person{
Name: "Alice",
Age: 30,
Address: Address{
City: "New York",
Country: "USA",
},
}
// JSON 编码
jsonData, err := json.Marshal(person)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(jsonData)) // 输出: {"name":"Alice","age":30,"address":{"city":"New York","country":"USA"}}
// JSON 解码
jsonString := `{"name":"Bob","age":25,"address":{"city":"San Francisco","country":"USA"}}`
var newPerson Person
err = json.Unmarshal([]byte(jsonString), &newPerson)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(newPerson) // 输出: {Bob 25 {San Francisco USA}}
}总结
四件事记住就够了:字段名靠 json:"..." 标签对齐,不写标签就用字段名原样;omitempty 让零值字段在输出里消失;结构不确定时用 map[string]interface{} 兜底,但要记得 JSON 里的数字到 Go 这边统一变成 float64;循环引用的结构体会让编码报错。