Go 语言 JSON 序列化与反序列化

Go 语言 JSON 序列化与反序列化

前言

在编程里,不同语言(Go、Java、Python、前端JS)要互相传递数据,必须用一种大家都认识的通用格式

JSON 就是目前全世界最流行、最简单、最通用的格式。

本文从JSON 底层本质、数据类型、语法规则 讲起,再到 Go 序列化/反序列化完整实战,包含字节级转换、内存地址变化、标签细节、易错点、底层原理,零基础可直接看懂、直接复制运行。


一、什么是 JSON?(最直白解释)

JSON = JavaScript Object Notation

你不用管英文全称,只需要记住一句话:

JSON 本质就是一段有固定格式的纯文本字符串,用来存储和传输数据。

它的特点:

  • 纯文本,任何语言都能读、能解析
  • 格式简单、体积小,网络传输速度快
  • 前后端、服务器、APP、小程序通用标准
  • 人类能直接看懂,方便调试
  • 底层在计算机中就是字节数组(数字)

小知识点:JSON 不是对象、不是结构体,只是字符串,程序拿到后解析成对应语言的对象。


二、JSON 支持哪些数据类型?(必须背下来)

JSON 一共只支持 6 种数据类型 ,没有第七种!

不支持日期、函数、正则、undefined 等类型。

  1. 对象(Object) → 用 { }
  2. 数组(Array) → 用 [ ]
  3. 字符串(String)
  4. 数字(Number)
  5. 布尔值(true / false)
  6. 空值(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 对应 JSON null"" 是空字符串。


四、JSON 必须遵守的规则(注意点!错一个就解析失败)

  1. key 必须用英文双引号,不能单引号、不加引号
  2. 字符串必须英文双引号
  3. 不能有多余逗号:最后一个键值对/数组元素后不能加逗号
  4. 不能写注释:// 单行注释、/* */ 多行注释都不支持
  5. 严格大小写敏感Namename 是两个完全不同的 key
  6. 符号全部用英文半角符号:逗号、冒号、括号,不能用中文符号
  7. 只支持上面 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

字段值为默认零值(""0falsenil)时,JSON 中不显示该字段:

go 复制代码
Score int `json:"score,omitempty"`

细节:omitempty 只作用于序列化,反序列化不受影响。

完整标签示例

go 复制代码
type Student struct {
	Name  string `json:"name"`
	Age   int    `json:"age,omitempty"`
	Pwd   string `json:"-"`
}

十二、最常见错误总结(必看+细节原因)

  1. 结构体字段小写:外部包无法访问,字段丢失
  2. JSON 使用单引号/中文符号:语法错误,解析直接失败
  3. JSON 最后字段加逗号:严格语法校验报错
  4. 反序列化不传地址 &:值拷贝,无法赋值,结果为空
  5. JSON key 与结构体不匹配 :用 json:"xxx" 标签映射
  6. JSON 写注释:JSON 不支持注释,解析失败
  7. 混淆 null 和空字符串nil 对应 null"" 是空字符串

十三、最终总结(超清晰·重点提炼)

  1. JSON 本质:一段带格式的纯文本字符串,底层是字节数组(ASCII 数字)
  2. JSON 6 种数据类型:对象、数组、字符串、数字、布尔、null,无其他类型
  3. JSON 语法铁律:双引号、英文符号、无注释、无尾逗号、大小写敏感
  4. Go 依赖标准库 encoding/json,无需第三方包
  5. 序列化 :Go 内存数据 → JSON 字符串;反序列化:JSON → Go 内存数据
  6. 转换是数据拷贝,内存地址变化,不是引用
  7. 结构体字段必须大写才能被 json 包识别
  8. json:"标签" 自定义键名、忽略字段、空值隐藏,适配前端接口规范

相关推荐
罗超驿1 小时前
6.Java多线程详解:Thread类、线程属性与start()方法深度解析
java·开发语言·面试·java-ee
海的透彻1 小时前
jmeter预制处理器JSR223-加解密
开发语言·jmeter·sm2·jsr233
asyxchenchong8881 小时前
R+VIC 模型融合实践技术应用及未来气候变化模型预测
开发语言·r语言
Run_Teenage1 小时前
算法模板:输入输出,并查集
java·开发语言·算法
古城小栈2 小时前
Rust Tauri:构建轻量高性能跨平台桌面应用
开发语言·后端·rust
Chase_______2 小时前
【Java杂项】String 为什么不可变?从对象引用、常量池到字符串拼接讲清楚
java·开发语言
xwjalyf2 小时前
javascript数组 forEach,filter,some,every,map,find,reduce的用法与区别
开发语言·javascript·json·ecmascript
qq_2518364572 小时前
基于java Web 耗材购置与维修网络申报审批系统设计与实现
java·开发语言·前端
AI玫瑰助手2 小时前
Python函数:def定义函数与参数传递基础
android·开发语言·python