📚 原创系列: “Go语言学习系列”
🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。
🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Go技术文章。
📑 Go语言学习系列导航
🚀 第二阶段:基础巩固篇本文是【Go语言学习系列】的第17篇,当前位于第二阶段(基础巩固篇)
- 13-包管理深入理解
- 14-标准库探索(一):io与文件操作
- 15-标准库探索(二):字符串处理
- 16-标准库探索(三):时间与日期
- 17-标准库探索(四):JSON处理 👈 当前位置
- 18-标准库探索(五):HTTP客户端
- 19-标准库探索(六):HTTP服务器
- 20-单元测试基础
- 21-基准测试与性能剖析入门
- 22-反射机制基础
- 23-Go中的面向对象编程
- 24-函数式编程在Go中的应用
- 25-context包详解
- 26-依赖注入与控制反转
- 27-第二阶段项目实战:RESTful API服务
📖 文章导读
在本文中,您将了解:
- JSON的基本概念及其在Go中的类型映射
- 使用encoding/json包进行基本序列化与反序列化
- JSON标签的使用技巧与嵌套结构处理
- 高效处理大型JSON数据的流式处理方法
- 自定义JSON编码与解码的高级技术
- 处理复杂场景如多态JSON和动态字段名
- JSON处理的性能优化最佳实践
JSON作为一种轻量级数据交换格式,在Web应用、API和配置系统中被广泛使用。Go语言提供了功能完善的JSON处理库,本文将带您全面掌握这一关键技能。
1. JSON基础知识
在深入Go的JSON处理之前,让我们先简要回顾一下JSON的基础知识。
1.1 JSON数据类型
JSON支持以下数据类型:
- 数字:整数或浮点数,如
123或3.14 - 字符串:用双引号包围的文本,如
"Hello, World" - 布尔值:
true或false - 数组:有序的值集合,用方括号表示,如
[1, 2, 3] - 对象:键值对集合,用花括号表示,如
{"name": "John", "age": 30} - null:表示空值,如
{"address": null}
1.2 JSON与Go类型的映射
Go的encoding/json包会根据以下规则在JSON和Go类型之间进行映射:
| JSON类型 | Go类型 |
|---|---|
| 对象 | struct, map[string]T |
| 数组 | slice, array |
| 字符串 | string |
| 数字 | int, int8, …, int64, uint, uint8, …, uint64, float32, float64 |
| 布尔值 | bool |
| null | nil |
2. 基本序列化与反序列化
Go提供了简单的函数将Go对象转换为JSON(序列化/Marshal),以及将JSON转换回Go对象(反序列化/Unmarshal)。
2.1 序列化Go结构体到JSON
使用json.Marshal函数可以将Go结构体转换为JSON字节切片:
package main
import (
"encoding/json"
"fmt"
)
// 定义一个Person结构体
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email"`
Hobbies []string `json:"hobbies"`
}
func main() {
// 创建一个Person实例
person := Person{
Name: "张三",
Age: 28,
Email: "zhangsan@example.com",
Hobbies: []string{
"读书", "旅行", "编程"},
}
// 序列化为JSON
jsonData, err := json.Marshal(person)
if err != nil {
fmt.Println("序列化失败:", err)
return
}
// 打印JSON字符串
fmt.Println(string(jsonData))
}
输出:
{
"name":"张三","age":28,"email":"zhangsan@example.com","hobbies":["读书","旅行","编程"]}
为了使输出的JSON更易读,可以使用json.MarshalIndent添加缩进:
// 带缩进的序列化
jsonIndent, err := json.MarshalIndent(person, "", " ")
if err != nil {
fmt.Println("序列化失败:", err)
return
}
fmt.Println(string(jsonIndent))
输出:
{
"name": "张三",
"age": 28,
"email": "zhangsan@example.com",
"hobbies": [
"读书",
"旅行",
"编程"
]
}
2.2 反序列化JSON到Go结构体
使用json.Unmarshal函数可以将JSON字节切片转换为Go结构体:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email"`
Hobbies []string `json:"hobbies"`
}
func main() {
// JSON字符串
jsonStr := `{
"name": "李四",
"age": 32,
"email": "lisi@example.com",
"hobbies": ["摄影", "游泳", "烹饪"]
}`
// 创建一个Person变量用于存储反序列化的结果
var person Person
// 反序列化
err := json.Unmarshal([]byte(jsonStr), &person)
if err != nil {
fmt.Println("反序列化失败:", err)
return
}
// 打印结果
fmt.Printf("姓名: %s\n", person.Name)
fmt.Printf("年龄: %d\n", person.Age)
fmt.Printf("邮箱: %s\n", person.Email)
fmt.Printf("爱好: %v\n", person.Hobbies)
}
输出:
姓名: 李四
年龄: 32
邮箱: lisi@example.com
爱好: [摄影 游泳 烹饪]
2.3 处理未知结构的JSON
在某些情况下,我们可能不知道JSON的确切结构。这时可以使用map[string]interface{}或interface{}来解析JSON:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 一个未知结构的JSON
jsonStr := `{
"name": "王五",
"details": {
"city": "北京",
"postal_code": "100000"
},
"scores": [95, 89, 75],
"graduated": true
}`
// 解析为map[string]interface{}
var result map[string]interface{
}
err := json.Unmarshal([]byte(jsonStr), &result)
if err != nil {
fmt.Println("反序列化失败:", err)
return
}
// 访问反序列化后的数据
fmt.Println("姓名:", result["name"])
// 获取嵌套字段需要类型断言
details := result["details"].(map[string]interface{
})
fmt.Println("城市:", details["city"])
fmt.Println("邮编:", details["postal_code"])
// 获取数组
scores := result["scores"].([]interface{
})
fmt.Printf("成绩: %.0f %.0f %.0f\n", scores[0], scores[1], scores[2])
// 获取布尔值
graduated := result["graduated"].(bool)
fmt.Println("是否毕业:", graduated)
}
输出:
姓名: 王五
城市: 北京
邮编: 100000
成绩: 95 89 75
是否毕业: true
2.4 序列化与反序列化Map
除了结构体,我们还可以直接序列化和反序列化Map:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 创建一个map
userMap := map[string]interface{
}{
"name": "赵六",
"age": 42,
"is_admin": true,
"contact": map[string]string{
"phone": "13812345678",
"email": "zhaoliu@example.com",
},
}
// 序列化map
jsonData, err := json.MarshalIndent(userMap, "", " ")
if err != nil {
fmt.Println("序列化失败:", err)
return
}
fmt.Println("序列化结果:")
fmt.Println(string(jsonData))
// 反序列化回map
var newUserMap map[string]interface{
}
err = json.Unmarshal(jsonData, &newUserMap)
if err != nil {
fmt.Println("反序列化失败:", err)
return
}
fmt.Println("\n反序列化结果:")
fmt.Printf("姓名: %s\n", newUserMap["name"])
fmt.Printf("年龄: %.0f\n", newUserMap["age"])
fmt.Printf("是否管理员: %t\n", newUserMap["is_admin"])
contact := newUserMap["contact"].(map[string]interface{
})
fmt.Printf("电话: %s\n", contact["phone"])
fmt.Printf("邮箱: %s\n", contact["email"])
}
输出:
序列化结果:
{
"age": 42,
"contact": {
"email": "zhaoliu@example.com",
"phone": "13812345678"
},
"is_admin": true,
"name": "赵六"
}
反序列化结果:
姓名: 赵六
年龄: 42
是否管理员: true
电话: 13812345678
邮箱: zhaoliu@example.com
3. JSON标签
Go使用结构体标签来控制字段如何序列化和反序列化。这些标签提供了强大的自定义能力。
3.1 基本JSON标签
type User struct {
UserName string `json:"username"` // 重命名字段
Password string `json:"-"` // 完全忽略该字段
Email string `json:"email,omitempty"` // 如果为零值则忽略
CreatedAt time.Time `json:"created_at"` // 重命名并保留大小写
}
标签选项说明:
json:"fieldname"- 设置JSON中的字段名(默认使用结构体字段名)json:"-"- 在序列化时忽略此字段json:",omitempty"- 如果字段为空值(零值),则不包含在JSON输出中json:"fieldname,omitempty"- 组合使用重命名和omitempty
示例:
package main
import (
"encoding/json"
"fmt"
"time"
)
type User struct {
UserName string `json:"username"`
Password string `json:"-"`
Email string `json:"email,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
}
func main() {
// 创建一个User实例
user := User{
UserName: "gopher123",
Password: "secret123",
Email: "", // 空字符串是string的零值
CreatedAt: time.Now(),
// UpdatedAt未设置,将是零值
}
// 序列化
data, _ := json.MarshalIndent(user, "", " ")
fmt.Println(string(data))
}
输出:
{
"username": "gopher123",
"created_at": "2023-05-25T15:30:45.123456789+08:00"
}
注意:
Password字段被完全忽略Email由于是零值且有omitempty标签,所以不出现在JSON中UpdatedAt是零时间且有omitempty标签,所以不出现在JSON中
3.2 自定义字段名
除了基本的重命名,JSON标签还允许使用特殊字符:
type Product struct {
ID int `json:"id"`
Name string `json:"product-name"` // 使用连字符
Price float64 `json:"price($)"` // 使用特殊字符
Description string `json:"desc.detailed"` // 使用点
}
3.3 处理嵌套结构体
标签也适用于嵌套结构体:
package main
import (
"encoding/json"
"fmt"
)
type Address struct {
Street string `json:"street"`
City string `json:"city"`
Country string `json:"country"`
}
type Employee struct {
Name string `json:"name"`
Age int `json:"age,omitempty"`
Address Address `json:"address"` // 嵌套结构体
}
func main() {
emp := Employee{
Name: "李明",
Age: 0, // 将被忽略
Address: Address{
Street: "朝阳路123号",
City: "北京",
Country: "中国",
},
}
data

最低0.47元/天 解锁文章
1477

被折叠的 条评论
为什么被折叠?



