互联网协议必备:Go语言中JSON的序列化与反序列化

简介: 互联网协议必备:Go语言中JSON的序列化与反序列化

/ Go 语言将结构体序列化和存储为 JSON 数据指南 /


 

一、概述

JSON 是一种非常流行的数据交换格式,在 Go 语言中经常需要将结构体实例序列化为 JSON 字符串保存和传输。本文将详细介绍如何使用 Go 语言中的 encoding/json 包将结构体序列化为 JSON 数据。

主要内容包括:

  • JSON 序列化概述
  • 定义可序列化的结构体
  • 使用 json.Marshal 序列化
  • 定制序列化逻辑
  • 使用 json.Unmarshal 反序列化
  • 处理空值和默认值
  • 嵌套对象和匿名字段
  • 序列化 interface{}
  • stream 编码器
  • JSON 与 XML 对比
  • 文本格式的优点
  • 使用规范
  • 序列化性能优化

通过本文可以全面了解 Go 语言中处理 JSON 的知识,及如何使用 json 包将结构体快速序列化为 JSON。


 

二、JSON 序列化概述

JSON 是一种非常流行的结构化数据格式,被广泛用于各种网络传输和数据存储之中。

Go 语言中主要通过 json 包来处理 JSON 数据,其中两个核心函数:

  • json.Marshal 序列化 Go 对象为 JSON 格式字节流
  • json.Unmarshal JSON 数据解析为 Go 对象

十分方便地实现序列化与反序列化。


 

三、定义可序列化的结构体

要将一个结构体实例序列化为 JSON,需要确保结构体字段可被 json 包访问,通常有两种方式:

  1. 字段首字母大写,则可以被外部包访问

  2. 通过 tag 指定 json 字段名

一个例子:

type User struct {
  ID int `json:"id"`
  Name string `json:"name"` 
  Age int
}


这样 User 结构体可以正确序列化和反序列化。


 

四、使用 json.Marshal 序列化

将对象编码为 JSON 字节流十分简单:

package main
import (
  "encoding/json"
  "fmt"
)
type User struct {
  Name string `json:"name"`
  Age  int    `json:"age"`
}
func main() {
  user := User{"John", 20}
  // 序列化
  buf, err := json.Marshal(user)
  if err != nil {
    fmt.Println(err)
    return
  }
  // 打印JSON
  fmt.Println(string(buf))
}


运行结果:

{"name":"John","age":20}

通过 json.Marshal 即可将结构体序列化。


 

五、定制序列化逻辑

如果需要控制序列化的细节,可以实现 Marshaler 接口:

package main
import (
  "encoding/json"
  "fmt"
)
type User struct {
  Name string `json:"name"`
  Age  int    `json:"age"`
}
// 实现Marshaler接口
func (u User) MarshalJSON() ([]byte, error) {
  return []byte(fmt.Sprintf(`{"name":"%s"}`, u.Name)), nil
}
func main() {
  u := User{"John", 20}
  jsonBytes, _ := json.Marshal(u)
  fmt.Println(string(jsonBytes)) // {"name":"John"}
}


这样我们可以灵活控制各字段的编码过程。


 

六、使用 json.Unmarshal 反序列化

对于反序列化 JSON 解析为 Go 对象,可以简单的调用 json.Unmarshal:

package main
import (
  "encoding/json"
  "fmt"
)
type User struct {
  Name string `json:"name"`
  Age  int    `json:"age"`
}
func main() {
  jsonStr := `{"name":"John","age":20}`
  // 反序列化到user
  var user User
  err := json.Unmarshal([]byte(jsonStr), &user)
  if err != nil {
    fmt.Println(err)
    return 
  }
  fmt.Println(user.Name) // John
  fmt.Println(user.Age) // 20 
}

传入 JSON 数据以及对象指针,即可解析为对应对象。


 

七、处理空值和默认值

对于空值的序列化,默认会忽略空值字段:

type User struct {
  Name string
  Age  int
}
u := User{"John", 0} 
jsonBytes, _ := json.Marshal(u)
fmt.Println(string(jsonBytes)) // {"Name":"John"}

但可以通过定义 MarshalJSON 方法自定义处理空值的编码。

反序列化时,需要处理不存在的字段,可以使用指针或默认值。


 

八、嵌套对象和匿名字段

对于嵌套的对象字段和内嵌匿名类型,也可以正常序列化和反序列化:

type Info struct {
  Addr string
}
type User struct {
  Info // 嵌套Info
  Name string
}
u := User{Info{"Beijing"}, "Tom"}
jsonBytes, _ := json.Marshal(u)
fmt.Println(string(jsonBytes)) // {"Addr":"Beijing","Name":"Tom"}

编码后嵌套的类型将被内联到结果中。


 

九、序列化 interface{}

json 包可以处理任意的 interface{}值:

data := map[string]interface{}{
  "name": "John",
  "age": 20,
}
jsonBytes, _ := json.Marshal(data)
fmt.Println(string(jsonBytes))

但编码结果会包含类型信息和 Base64 编码后的值。这比定制序列化更低效。


 

十、stream 编码器

我们也可以使用 streaming 模式的 encoder 来进行序列化:


package main
import (
  "encoding/json"
  "os"
)
func main() {
  type User struct {
    Name string
    Age  int
  }
  user := User{"John", 20}
  // 使用stream encoder
  enc := json.NewEncoder(os.Stdout)
  enc.Encode(user)
}

这种编码方式非常灵活,可实现各种自定义输出格式。


 

十一、JSON 与 XML 对比

相比 XML,JSON 具有以下优点:

  • 读取和解析更加方便快捷
  • 存储占用更小且具有可读性
  • 支持更多语言与平台
  • 性能和解析速度更快

这使得 JSON 成为主流的数据交换格式。


 

十二、文本格式的优点

JSON 与其他二进制序列化格式相比,文本格式 JSON 有以下优势:

  • 可读性强,便于打印调试
  • 可复用现有文本处理工具
  • 不依赖语言和平台
  • 可支持部分更新修改

这些特点使 JSON 很适合作为配置、通信和数据存储格式。


 

十三、使用规范

使用 JSON 时,也应注意遵守一定规范:

  • 格式化打印 JSON 数据
  • 为类型和字段定义 Canonical 名称
  • 提供完整的文档
  • 版本控制 JSON 接口
  • 采用更灵活的 schema 描述

这可以确保使用 JSON 的稳定性和兼容性。


 

十四、序列化性能优化

对于性能敏感的场景,可以通过以下技巧优化序列化:

  • 提前指定好对象大小
  • 重用分配的内存
  • 尽量避免解析嵌套数据
  • 按需完成自定义编码

针对特定用例微优化这些方面可以提升 JSON 处理的性能。


 

十五、总结

JSON 作为平台无关的数据交换格式,在 Go 语言中有着广泛的应用。本文详细介绍了 Go 语言序列化结构体为 JSON 的知识,希望大家可以运用自如。

管理和转换 JSON 是后端开发中经常进行的工作,这些知识也可以应用到许多其他语言中。掌握好这些技能,可以大大提高我们的开发效率。



目录
相关文章
|
18天前
|
Go
go语言中的数据类型
go语言中的数据类型
13 0
|
24天前
|
Go 开发者
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
|
24天前
|
安全 Go
掌握Go语言:Go语言通道,并发编程的利器与应用实例(20)
掌握Go语言:Go语言通道,并发编程的利器与应用实例(20)
|
24天前
|
存储 缓存 安全
掌握Go语言:Go语言中的字典魔法,高效数据检索与应用实例解析(18)
掌握Go语言:Go语言中的字典魔法,高效数据检索与应用实例解析(18)
|
5天前
|
数据采集 存储 Go
使用Go语言和chromedp库下载Instagram图片:简易指南
Go语言爬虫示例使用chromedp库下载Instagram图片,关键步骤包括设置代理IP、创建带代理的浏览器上下文及执行任务,如导航至用户页面、截图并存储图片。代码中新增`analyzeAndStoreImage`函数对图片进行分析和分类后存储。注意Instagram的反爬策略可能需要代码适时调整。
使用Go语言和chromedp库下载Instagram图片:简易指南
|
24天前
|
存储 安全 Go
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
|
2天前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello("Alice")`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
10 1
|
2天前
|
人工智能 Go 调度
掌握Go并发:Go语言并发编程深度解析
掌握Go并发:Go语言并发编程深度解析
|
2天前
|
SQL 关系型数据库 MySQL
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
|
3天前
|
Go 开发者
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
【4月更文挑战第21天】本文介绍了Go语言中的流程控制语句,包括`if`、`switch`和`for`循环。`if`语句支持简洁的语法和初始化语句,但需注意比较运算符的使用。`switch`语句提供多分支匹配,可省略`break`,同时支持不带表达式的形式。`for`循环有多种形式,如基本循环和`for-range`遍历,遍历时修改原集合可能导致未定义行为。理解并避免易错点能提高代码质量和稳定性。通过实践代码示例,可以更好地掌握Go语言的流程控制。
11 3
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解