Go 语言 _JSON---序列化与反序列化

一、JSON 基础概念

  1. JSON 全称:JavaScript Object Notation,轻量级数据交换格式
  2. 特性:跨平台、跨语言、可读性强,前后端 / 服务间通用数据传输标准
  3. Go 操作 JSON 标准库:encoding/json
  4. Go 处理 JSON 两种核心方案:
    • 结构体 struct:结构固定、类型安全,推荐业务开发使用
    • map string interface {}:结构动态、适配未知 JSON 字段

二、核心前置规则(必记踩坑点)

  1. 结构体字段首字母必须大写 小写字段为包内私有,json.Marshal 无法反射读取,会直接忽略该字段。
  2. 结构体标签 struct tag 控制 JSON 输出规则
  3. 反序列化 json.Unmarshal 第二个参数必须传变量地址,否则修改无效
  4. map 解析 JSON 后所有数值默认类型为 float64,数组为 []interface{},需要类型断言取值

三、通过结构体生成 JSON(序列化 Marshal)

3.1 基础无标签示例

go

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

// IT 行业信息结构体
type IT struct {
	Company  string
	Subjects []string
	IsOk     bool
	Price    float64
}

func main() {
	// 结构体实例赋值
	s := IT{
		Company:  "itcast",
		Subjects: []string{"Go", "C++", "Python", "Test"},
		IsOk:     true,
		Price:    666.666,
	}

	// json.Marshal:结构体 → JSON字节流
	buf, err := json.Marshal(s)
	if err != nil {
		fmt.Println("序列化失败 err = ", err)
		return
	}
	// 字节切片转字符串打印
	fmt.Println("JSON结果 = ", string(buf))
}

输出结果:

plaintext

Go 复制代码
JSON结果 =  {"Company":"itcast","Subjects":["Go","C++","Python","Test"],"IsOk":true,"Price":666.666}

3.2 struct tag 标签语法详解(重点)

标签格式:json:"自定义key,附加选项"

表格

标签写法 作用说明
json:"-" 忽略此字段,不序列化输出到 JSON
json:"subjects" 自定义 JSON 字段名(二次命名,统一前后端小写命名规范)
json:",string" 将数字 / 布尔值序列化为字符串格式输出

带标签完整结构体示例:

go

Go 复制代码
type IT struct {
	Company  string  `json:"-"`          // 忽略该字段
	Subjects []string`json:"subjects"`  // 自定义json key
	IsOk     bool    `json:",string"`    // bool转字符串
	Price    float64 `json:",string"`    // 浮点数转字符串
}

四、通过 map 生成 JSON(动态结构序列化)

适合字段不固定、无法提前定义结构体的场景,使用 map[string]interface{} 存储任意类型值

go

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	// 创建map,预分配4个空间
	m := make(map[string]interface{}, 4)
	m["company"] = "itcast"
	m["subjects"] = []string{"GO", "C++", "Python", "Test"}
	m["isok"] = true
	m["price"] = 666.666

	// map序列化
	result, err := json.Marshal(m)
	if err != nil {
		fmt.Println("序列化失败 err = ", err)
		return
	}
	fmt.Println("map转JSON = ", string(result))
}

五、JSON 反序列化 Unmarshal:JSON → Go 变量

5.1 JSON 解析到结构体(固定结构首选)

流程:JSON 字符串转[]byte → 传入json.Unmarshal,传入结构体地址

go

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

// 标签和json字段一一对应
type IT struct {
	Company  string  `json:"company"`
	Subjects []string`json:"subjects"`
	IsOk     bool    `json:"isok"`
	Price    float64 `json:"price"`
}

func main() {
	// 原始JSON字符串
	jsonBuf := `{
		"company": "itcast",
		"subjects": ["GO","C++","Python","Test"],
		"isok":true,
		"price": 666.666
	}`

	var tmp IT
	// 第二个参数必须传 &tmp 地址
	err := json.Unmarshal([]byte(jsonBuf), &tmp)
	if err != nil {
		fmt.Println("反序列化失败 err = ", err)
		return
	}
	fmt.Printf("解析结构体完整数据:%+v\n", tmp)
	fmt.Println("公司名称:", tmp.Company)
}

5.2 JSON 解析到 map(动态未知 JSON 结构)

解析后 map 中 value 类型统一为 interface{},必须通过类型断言才能正常取值

go

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	jsonBuf := `{
		"company": "itcast",
		"subjects": ["GO","C++","Python","Test"],
		"isok":true,
		"price": 666.666
	}`

	// 定义map接收json
	m := make(map[string]interface{}, 4)
	err := json.Unmarshal([]byte(jsonBuf), &m)
	if err != nil {
		fmt.Println("反序列化失败 err = ", err)
		return
	}
	fmt.Printf("原始map数据:%+v\n", m)

	// 直接取值会报错,需要类型断言
	// str := m["company"] 错误写法

	// 方式1:简单断言单个字段
	if companyStr, ok := m["company"].(string); ok {
		fmt.Println("company =", companyStr)
	}

	// 方式2:switch type 批量类型判断(通用推荐)
	for key, value := range m {
		fmt.Printf("键:%s,原始值:%v\n", key, value)
		switch data := value.(type) {
		case string:
			fmt.Printf("【%s】类型:string,值:%s\n", key, data)
		case bool:
			fmt.Printf("【%s】类型:bool,值:%v\n", key, data)
		case float64:
			fmt.Printf("【%s】类型:float64,值:%f\n", key, data)
		case []interface{}:
			fmt.Printf("【%s】类型:数组[]interface{},值:%v\n", key, data)
		default:
			fmt.Printf("【%s】未知类型\n", key)
		}
	}
}

六、高频易错点汇总(面试 / 开发踩坑清单)

  1. 结构体字段小写:反射无法读取,序列化直接丢失字段
  2. 反序列化不传地址:Unmarshal 无法修改变量值,解析后数据为空
  3. map 解析 JSON 数值自动是 float64,不能直接转 int,必须断言后强转
  4. json:",string" 标签只作用序列化,反序列化带引号数字也能自动匹配数值类型
  5. json:"-" 标签字段序列化忽略,反序列化也不会赋值
  6. JSON 字符串必须使用反引号 `````,单引号不能包裹多行字符串(Go 语法限制)

七、两种方案使用场景对比

表格

方案 优点 缺点 使用场景
struct 结构体 类型安全、代码易读、字段校验、IDE 提示 需提前定义结构,适配动态 JSON 麻烦 接口参数、固定格式返回数据
mapstringinterface{} 无需定义结构,适配任意 JSON 无类型提示,频繁类型断言,代码臃肿 未知第三方 JSON、动态配置数据

八、全文总结

  1. 序列化(Go → JSON):json.Marshal(),支持 struct /map
  2. 反序列化(JSON → Go):json.Unmarshal(),支持 struct /map
  3. struct tag 是自定义 JSON 输出的核心工具,掌握 -、自定义 key、,string 三种常用写法
  4. map 解析 JSON 核心难点:interface{} 类型断言
  5. 业务开发优先使用结构体,动态不确定 JSON 再选用 map
相关推荐
麻瓜老宋19 小时前
AI开发C语言应用按步走,表达式计算器calc的第四步,交互式 REPL 模式
c语言·开发语言·atomcode
猫猫不是喵喵.19 小时前
双亲委派机制与类加载过程
java·开发语言
布朗克16819 小时前
Go入门到精通-22-同步原语
开发语言·后端·golang·同步原语
帅次19 小时前
Kotlin Flow 与 StateFlow:UI 单向数据流基础
开发语言·ui·kotlin·stateflow·onlifecycle
艾伦野鸽ggg20 小时前
JavaScript 浏览器本地存储
开发语言·javascript·ecmascript
杜子不疼.20 小时前
【Qt常用控件】WindowTitle 与 WindowIcon:窗口标题和图标
开发语言·qt
AOwhisky20 小时前
Python 学习笔记(第十一期)——运维自动化(上·后篇):进程级监控与子进程管理——psutil进阶
运维·开发语言·python·学习·云原生·运维开发
人工干智能21 小时前
Python 的链式调用:一连串的“点”调用
开发语言·python
怒放de生命201021 小时前
【web3基础】go-zero使用etcd实现服务注册与发现(四)
golang·web3·etcd·go-zero