Go 语言 JSON 序列化与反序列化
前言
在编程里,不同语言(Go、Java、Python、前端JS)要互相传递数据,必须用一种大家都认识的通用格式 。
JSON 就是目前全世界最流行、最简单、最通用的格式。
本文从JSON 底层本质、数据类型、语法规则 讲起,再到 Go 序列化/反序列化完整实战,包含字节级转换、内存地址变化、标签细节、易错点、底层原理,零基础可直接看懂、直接复制运行。
一、什么是 JSON?(最直白解释)
JSON = JavaScript Object Notation
你不用管英文全称,只需要记住一句话:
JSON 本质就是一段有固定格式的纯文本字符串,用来存储和传输数据。
它的特点:
- 纯文本,任何语言都能读、能解析
- 格式简单、体积小,网络传输速度快
- 前后端、服务器、APP、小程序通用标准
- 人类能直接看懂,方便调试
- 底层在计算机中就是字节数组(数字)
小知识点:JSON 不是对象、不是结构体,只是字符串,程序拿到后解析成对应语言的对象。
二、JSON 支持哪些数据类型?(必须背下来)
JSON 一共只支持 6 种数据类型 ,没有第七种!
不支持日期、函数、正则、undefined 等类型。
- 对象(Object) → 用
{ } - 数组(Array) → 用
[ ] - 字符串(String)
- 数字(Number)
- 布尔值(true / false)
- 空值(null)
三、JSON 6 种数据类型详解(带例子+细节注意)
1. 对象(Object)
- 用大括号
{}包裹 - 里面是 键值对(key: value),键值对之间用英文逗号分隔
- key 必须是双引号字符串,不能是数字、不能不加引号
- 键值之间用英文冒号
:,不能用中文冒号
例子:
json
{
"name": "Alice",
"age": 18,
"isStudent": true
}
2. 数组(Array)
- 用中括号
[]包裹 - 里面可以放任意 JSON 支持的类型,元素用英文逗号分隔
- 数组元素可以是对象、字符串、数字、布尔等,支持混合(不推荐)
例子:
json
[10, 20, 30, 40]
对象数组(最常用):
json
[
{ "name": "Alice" },
{ "name": "Bob" }
]
3. 字符串(String)
- 必须用 英文双引号
" " - 不能用单引号
' '、反引号 `````,JSON 语法严格禁止 - 字符串内部特殊字符需要转义:
\"\\\n等
正确:
json
"name": "Alice"
错误:
json
"name": 'Alice' // 错!JSON 不支持单引号
4. 数字(Number)
- 支持整数、负数、小数
- 不需要加引号,加引号会变成字符串类型
- 不支持八进制、二进制格式
json
"age": 18,
"score": 95.5,
"temperature": -5
5. 布尔值(true / false)
- 必须全小写,不能 True、FALSE
- 不加引号,加引号会变成字符串
json
"isStudent": true
6. 空值 null
- 必须全小写
null,不能写 NULL、nil、空字符串 - 代表不存在、无数据 ,和空字符串
""完全不同
json
"remark": null
小细节:
null≠"",反序列化时 Go 中nil对应 JSONnull,""是空字符串。
四、JSON 必须遵守的规则(注意点!错一个就解析失败)
- key 必须用英文双引号,不能单引号、不加引号
- 字符串必须英文双引号
- 不能有多余逗号:最后一个键值对/数组元素后不能加逗号
- 不能写注释:// 单行注释、/* */ 多行注释都不支持
- 严格大小写敏感 :
Name和name是两个完全不同的 key - 符号全部用英文半角符号:逗号、冒号、括号,不能用中文符号
- 只支持上面 6 种数据类型,不支持其他类型
五、一个标准完整的 JSON 例子(包含所有类型+嵌套)
json
{
"name": "Alice",
"age": 18,
"score": 95.5,
"isPass": true,
"hobbies": ["read", "run"],
"class": {
"className": "Class One"
},
"remark": null
}
包含:对象、数组、字符串、数字、布尔、null、嵌套结构,是实际开发最常见格式。
六、现在进入 Go 部分:什么是序列化、反序列化?
核心底层理解
- Go 程序中的结构体、切片、map 是内存中的对象,只能 Go 语言使用;
- JSON 是通用字符串,所有语言都能识别;
- 两者互相转换就是序列化与反序列化。
1. 序列化(Marshal)
Go 内存数据 → JSON 字符串(字节数组)
作用:把 Go 里的数据转成 JSON,用于网络传输、接口返回、文件存储。
2. 反序列化(Unmarshal)
JSON 字符串 → Go 内存数据
作用:把接收的 JSON 解析成 Go 程序能直接操作的结构体、切片。
关键知识点:序列化/反序列化是数据拷贝,不是引用,转换后内存地址完全不同。
七、Go 操作 JSON 的基础包
Go 自带标准库,无需安装第三方依赖,开箱即用:
go
import "encoding/json"
小知识点:
json.Marshal返回[]byte字节数组,不是字符串,需要用string()转换。
八、超级清晰底层示例:英文切片序列化(字节级+内存地址对照)
使用纯英文数据,可清晰看到字节数字 ↔ JSON 字符一一对应,直观理解 JSON 本质。
完整可运行代码
go
package main
import (
"encoding/json"
"fmt"
)
func main() {
// -------------- 第一步:原始 Go 切片(纯英文) --------------
students := []string{"Alice", "Bob", "Charlie"}
fmt.Println("Original Go Slice:", students)
fmt.Printf("Original Slice Memory Address: %p\n\n", &students)
// -------------- 第二步:序列化 → JSON 字节数组 --------------
jsonBytes, err := json.Marshal(students)
if err != nil {
fmt.Println("Error:", err)
return
}
// 打印ASCII字节数组(数字对应JSON字符)
fmt.Println("Serialized JSON Bytes (ASCII Code):", jsonBytes)
// 字节数组转字符串 = 标准JSON
fmt.Println("Bytes Converted To String (JSON):", string(jsonBytes))
fmt.Printf("JSON Bytes Array Memory Address: %p\n\n", &jsonBytes)
// -------------- 第三步:反序列化 → 新的 Go 切片 --------------
var newStudents []string
err = json.Unmarshal(jsonBytes, &newStudents)
fmt.Println("Deserialized Go Slice:", newStudents)
fmt.Printf("New Slice Memory Address: %p\n\n", &newStudents)
// -------------- 第四步:遍历打印 --------------
fmt.Println("==== Iterate Over Deserialized Slice ====")
for index, value := range newStudents {
fmt.Printf("Index %d → Value: %s\n", index, value)
}
}
真实运行结果+ASCII对照
Original Go Slice: [Alice Bob Charlie]
Original Slice Memory Address: 0x140000a0000
Serialized JSON Bytes (ASCII Code): [91 34 65 108 105 99 101 34 44 34 66 111 98 34 44 34 67 104 97 114 108 105 101 34 93]
Bytes Converted To String (JSON): ["Alice","Bob","Charlie"]
JSON Bytes Array Memory Address: 0x140000a4000
Deserialized Go Slice: [Alice Bob Charlie]
New Slice Memory Address: 0x140000a0010
==== Iterate Over Deserialized Slice ====
Index 0 → Value: Alice
Index 1 → Value: Bob
Index 2 → Value: Charlie
ASCII 数字 ↔ 字符精准对应
91 → [
34 → "
65 → A
108 → l
105 → i
99 → c
101 → e
34 → "
44 → ,
93 → ]
核心结论:JSON 底层就是ASCII 字节数字,转成字符串后就是我们看到的 JSON 格式;转换前后内存地址不同,是全新数据。
九、Go 结构体转 JSON(序列化)
第一步:定义结构体(关键细节必看)
强制规则:结构体字段必须首字母大写!
- Go 中小写字段是私有字段,只能当前包访问;
encoding/json是标准库(外部包),无法读取小写字段;- 小写字段序列化会直接丢失,不会出现在 JSON 中。
同时支持嵌套结构体,完全对应 JSON 嵌套对象。
go
package main
import (
"encoding/json"
"fmt"
)
// 班级结构体(嵌套用)
type Class struct {
ClassName string
}
// 学生主结构体
type Student struct {
Name string // 姓名
Age int // 年龄
Score float64 // 分数
IsPass bool // 是否及格
Hobbies []string // 爱好(切片→JSON数组)
Class Class // 班级(嵌套结构体→JSON嵌套对象)
Remark interface{} // 备注(可存nil→JSON null)
}
第二步:序列化完整代码+输出
go
func main() {
stu := Student{
Name: "Alice",
Age: 18,
Score: 95.5,
IsPass: true,
Hobbies: []string{"read", "run"},
Class: Class{"Class One"},
Remark: nil,
}
// 普通序列化(紧凑一行)
jsonBytes, _ := json.Marshal(stu)
fmt.Println("Compact JSON:")
fmt.Println(string(jsonBytes))
// 格式化序列化(带换行缩进,便于阅读,开发调试常用)
jsonIndentBytes, _ := json.MarshalIndent(stu, "", " ")
fmt.Println("\nPretty JSON:")
fmt.Println(string(jsonIndentBytes))
}
输出结果
紧凑版:
json
{"Name":"Alice","Age":18,"Score":95.5,"IsPass":true,"Hobbies":["read","run"],"Class":{"ClassName":"Class One"},"Remark":null}
格式化版:
json
{
"Name": "Alice",
"Age": 18,
"Score": 95.5,
"IsPass": true,
"Hobbies": [
"read",
"run"
],
"Class": {
"ClassName": "Class One"
},
"Remark": null
}
小知识点:
json.MarshalIndent(对象, 前缀, 缩进),第二个参数是行前缀,第三个是缩进空格。
十、JSON 转 Go 结构体(反序列化)
核心强制规则:必须传结构体地址 &stu
- Go 中函数传参默认是值拷贝;
- 不传地址,Unmarshal 无法修改原变量,解析结果为空;
- 传地址
&变量,才能修改内存中的变量。
go
func main() {
// 标准JSON字符串
jsonStr := `{
"Name":"Alice",
"Age":18,
"Score":95.5,
"IsPass":true,
"Hobbies":["read","run"],
"Class":{"ClassName":"Class One"},
"Remark":null
}`
// 定义空结构体接收数据
var stu Student
// 反序列化:必须传 &stu
err := json.Unmarshal([]byte(jsonStr), &stu)
if err != nil {
fmt.Println("反序列化失败:", err)
return
}
fmt.Printf("反序列化结果:%+v\n", stu)
}
十一、JSON 标签(开发必备·细节全解)
Go 结构体字段默认大写,前端接口规范常用小写,通过 json:"标签" 自定义 JSON 行为,三种核心用法:
1. 自定义 JSON 键名(最常用)
go
type Student struct {
Name string `json:"name"` // JSON中key为小写name
Age int `json:"age"`
Score int `json:"score"`
}
输出:{"name":"Alice","age":18,"score":95}
2. 忽略字段:json:"-"
该字段不参与序列化,不会出现在 JSON 中,用于密码、内部标识等敏感字段:
go
Pwd string `json:"-"` // 序列化直接忽略
3. 空值不输出:omitempty
字段值为默认零值(""、0、false、nil)时,JSON 中不显示该字段:
go
Score int `json:"score,omitempty"`
细节:
omitempty只作用于序列化,反序列化不受影响。
完整标签示例
go
type Student struct {
Name string `json:"name"`
Age int `json:"age,omitempty"`
Pwd string `json:"-"`
}
十二、最常见错误总结(必看+细节原因)
- 结构体字段小写:外部包无法访问,字段丢失
- JSON 使用单引号/中文符号:语法错误,解析直接失败
- JSON 最后字段加逗号:严格语法校验报错
- 反序列化不传地址
&:值拷贝,无法赋值,结果为空 - JSON key 与结构体不匹配 :用
json:"xxx"标签映射 - JSON 写注释:JSON 不支持注释,解析失败
- 混淆
null和空字符串 :nil对应null,""是空字符串
十三、最终总结(超清晰·重点提炼)
- JSON 本质:一段带格式的纯文本字符串,底层是字节数组(ASCII 数字)
- JSON 6 种数据类型:对象、数组、字符串、数字、布尔、null,无其他类型
- JSON 语法铁律:双引号、英文符号、无注释、无尾逗号、大小写敏感
- Go 依赖标准库
encoding/json,无需第三方包 - 序列化 :Go 内存数据 → JSON 字符串;反序列化:JSON → Go 内存数据
- 转换是数据拷贝,内存地址变化,不是引用
- 结构体字段必须大写才能被 json 包识别
json:"标签"自定义键名、忽略字段、空值隐藏,适配前端接口规范