Go 语言大白话入门 12 - JSON:工作里最常见的数据格式

JSON:工作里最常见的数据格式

本文是《Go语言大白话入门》系列教程的第 12 篇。本文只做一件事:把 JSON 和 Go 里的结构体连起来,先能跑通,再去记名字。

本章目标

这一章看完,你至少要知道四件事:

  • JSON 是什么,平时拿来干什么;
  • 为什么 Go 里经常拿结构体配 JSON;
  • 结构体怎么转成 JSON,JSON 怎么再转回来;
  • 字段首字母大小写、json 标签这些坑到底怎么避。

常见坑位

先把坑摆在前面,后面看代码会省很多事。

  • 把 JSON 当成 Go 专属语法,其实它是程序之间通用的数据格式;
  • 看到像字典的文本就以为能直接用,忘了 JSON 必须合法;
  • 字段首字母写成小写,还指望 JSON 库照样能读到;
  • 搞不清 Marshal 和 Unmarshal 的方向;
  • 只盯着代码,不看错误信息。

JSON 是什么

可以把 JSON 理解成一种专门装数据的文本格式。

例如,一个用户信息可以写成这样:

json 复制代码
{
  "name": "小李",
  "age": 20,
  "city": "上海"
}

它看起来像一份排得很整齐的数据清单。说得再直白一点,JSON 不是 Go 的专用语法,而是很多程序都认的通用格式。

JSON 有什么用

JSON 最常见的作用,就是让不同程序之间方便交换数据。

常见场景有这些:

  • 前端把注册信息传给后端;
  • 后端把用户资料返回给前端;
  • 程序把配置内容存进文件;
  • 调第三方接口时接收返回结果。

你可以先把它理解成程序之间递来递去的一张数据纸条。能看懂这一点,后面学接口就会轻很多。

为什么 Go 常配合结构体使用 JSON

Go 里的结构体很适合表示一组有名字的数据,而 JSON 本身也是一组有名字的数据。

所以最常见的做法就是两种:

  • 用结构体接住 JSON;
  • 把结构体转成 JSON 发出去。

这样做有两个好处:

  • 不用自己手拼一大串字符串;
  • 代码更清楚,也更不容易出错。

先把这个来回记住就行:

  • 结构体可以变成 JSON;
  • JSON 也可以再变回结构体;
  • 后面写接口、读接口,基本都会碰到这套动作。

结构体转 JSON

先跑一个最小例子,看看结构体怎么变成 JSON。

go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
	City string `json:"city"`
}

func main() {
	user := User{Name: "小李", Age: 20, City: "上海"}

	data, err := json.Marshal(user)
	if err != nil {
		fmt.Println("转JSON失败:", err)
		return
	}

	fmt.Println(string(data))

	prettyData, err := json.MarshalIndent(user, "", "  ")
	if err != nil {
		fmt.Println("格式化JSON失败:", err)
		return
	}

	fmt.Println(string(prettyData))
}

这里有两个点先记住:

  • encoding/json 是 Go 标准库里专门处理 JSON 的包;
  • json.Marshal 的意思,就是把 Go 里的数据转换成 JSON。

结构体字段后面这段 json:"name" 叫结构体标签。它是在告诉 JSON 库:这个字段转出去以后,名字叫 name,不是 Name

运行后大概会看到这样的结果:

text 复制代码
{"name":"小李","age":20,"city":"上海"}
{
  "name": "小李",
  "age": 20,
  "city": "上海"
}

JSON 解析到结构体

再看反方向:把 JSON 字符串读进结构体。

go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
	City string `json:"city"`
}

func main() {
	jsonText := `{"name":"小李","age":20,"city":"上海"}`

	var user User
	err := json.Unmarshal([]byte(jsonText), &user)
	if err != nil {
		fmt.Println("解析失败:", err)
		return
	}

	fmt.Println(user.Name)
	fmt.Println(user.Age)
	fmt.Println(user.City)
}

这里最容易漏的是 &user。你可以先把它当成一个固定写法:只要函数要往你的变量里写数据,通常就要传指针,也就是带上 &

如果这里忘了加,常见报错就是 non-pointer。第一反应别想太多,先回头检查是不是把这个"口子"漏了。

json.Unmarshal 的作用可以先记成一句话:把 JSON 里的数据拆出来,再装回 Go 的结构体。

json 标签是做什么的

最常见的标签写法是这种:

go 复制代码
type User struct {
	Name  string `json:"name"`
	Email string `json:"email,omitempty"`
	Age   int    `json:"age,omitempty"`
}

这里分两层看:

  • name 是让 JSON 里的字段名和 Go 字段名分开;
  • omitempty 是说字段是零值时就别输出。

比如:

go 复制代码
u := User{Name: "小李"}

转出来的 JSON 就会更干净,只保留有值的字段。

text 复制代码
{"name":"小李"}

这个东西在接口返回里特别常见。没有的字段就别硬塞出来,返回会更清爽。

字段首字母大小写这个坑

这是新手最容易踩的地方。

go 复制代码
type User struct {
	name string `json:"name"`
}

这段写法通常不会得到你想要的结果。原因也不复杂:字段首字母小写时,Go 里这个字段对外部包不可见,JSON 库自然就拿不到它。

所以先记一条硬规则:只要这个字段要参与 JSON 处理,就写成大写开头。

换成大白话就是,字段不"露出来",外部库就看不见。JSON 库看不见,就没法帮你转。

嵌套结构体也能一起转

JSON 不只会处理单层数据。结构体里套结构体,它也能继续往下处理。

go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type Address struct {
	City string `json:"city"`
}

type User struct {
	Name    string  `json:"name"`
	Address Address `json:"address"`
}

func main() {
	user := User{
		Name: "小李",
		Address: Address{
			City: "上海",
		},
	}

	data, err := json.Marshal(user)
	if err != nil {
		fmt.Println("转JSON失败:", err)
		return
	}

	fmt.Println(string(data))
}

输出会像这样:

text 复制代码
{"name":"小李","address":{"city":"上海"}}

这就是为什么 JSON 很适合做接口数据:层级清楚,程序也能顺着层级往下读。

排查顺序

如果你这章跑不通,先按这个顺序查:

  1. 结构体字段首字母是不是大写;
  2. json 标签有没有写错;
  3. JSON 字符串里的引号、逗号、大括号是否完整;
  4. 你是不是把 Marshal 和 Unmarshal 的方向看反了;
  5. 函数要写入变量时,有没有漏掉 &

多数问题都能在这几步里找到,不用一上来就怀疑 Go 或怀疑自己。

练习任务

这章学完,建议自己动手做四件事:

  • 定义一个 Product 结构体,转成 JSON 打印出来;
  • 写一段 JSON 字符串,用 Unmarshal 解析回结构体;
  • 故意把字段首字母改成小写,看看结果;
  • 给某个字段加上 omitempty,看看零值时会不会被省掉。

本节最低通过线

这一章能过线,标准其实很简单:你能把 JSON 示例跑通,能说清字段是怎么对上的,也知道出问题先查哪一步。

前面这些内容学会以后,后面写 HTTP 接口、读配置文件、处理第三方返回值,都会顺很多。

本文章属于《Go语言大白话入门》零基础全套教程,持续更新、全程实操练手。 微信公众号【码栈研说】超前更新,目前比掘金领先 30 章内容,完整连载、无删减、持续日更,需要完整系统学习的读者可自行前往查看。

文中部分内容借助 AI 辅助梳理,全文人工校验、实操落地。

相关推荐
达达尼昂1 小时前
AI Native 工程实践:如何为 Claude 5 设计更有效的上下文
android·人工智能·后端
用户2930750976691 小时前
从零搭建 AI 日记助手:Milvus 向量数据库 + RAG 实战
后端
zguigo1 小时前
结合苍穹外卖对redis进行总结和教学
后端
玉宇夕落1 小时前
Milvus向量数据库和cs bs架构学习
后端
Larcher1 小时前
别再搞混了!一文讲透 AI Workflow 和 Agent 的本质区别
javascript·人工智能·后端
瀛川1 小时前
microVM 技术解析:面向多租户 Serverless 与 AI Agent 的微虚拟机架构
后端·云原生
Conan在掘金2 小时前
鸿蒙报错速查:装对象字量禁 as Object 编译炸,根因 + 真解法
后端
IT_陈寒2 小时前
Python的finally居然不等同于Go的defer,差点坑惨我
前端·人工智能·后端
会编程的土豆2 小时前
功能详解 01 · 用户注册:从表单到数据库一行
开发语言·数据库·后端·mysql·golang