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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 互联网协议必备: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 是后端开发中经常进行的工作,这些知识也可以应用到许多其他语言中。掌握好这些技能,可以大大提高我们的开发效率。



目录
相关文章
|
21天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
38 2
|
19天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
26 2
|
19天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
31 2
|
5天前
|
存储 Go 索引
go语言使用for循环遍历
go语言使用for循环遍历
21 7
|
8天前
|
存储 Go
go语言 遍历映射(map)
go语言 遍历映射(map)
22 2
|
9天前
|
Go 调度 开发者
Go语言中的并发编程:深入理解goroutines和channels####
本文旨在探讨Go语言中并发编程的核心概念——goroutines和channels。通过分析它们的工作原理、使用场景以及最佳实践,帮助开发者更好地理解和运用这两种强大的工具来构建高效、可扩展的应用程序。文章还将涵盖一些常见的陷阱和解决方案,以确保在实际应用中能够避免潜在的问题。 ####
|
9天前
|
测试技术 Go 索引
go语言使用 range 关键字遍历
go语言使用 range 关键字遍历
15 3
|
9天前
|
测试技术 Go 索引
go语言通过 for 循环遍历
go语言通过 for 循环遍历
19 3
|
11天前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
10天前
|
Go 索引
go语言按字符(Rune)遍历
go语言按字符(Rune)遍历
23 3