重学Go语言之JSON操作详解

 更新时间:2023年08月27日 08:25:19   作者:程序员读书  
JSON的全称是Javascript Object Notation,是一种数据结构化交互的标准协议,这篇文章主要是来和大家介绍一下Go语言中JSON的相关操作,需要的可以参考下

当你想在项目中保存配置时,你会选择什么数据格式,你可能会说用yaml或是json,当你作为后端要把数据返回给前端时,你会选择什么数据格式,这次我想,一定是json了吧。

关于在Go语言如何操作JSON,在这篇文章我们来探究一下!

JSON

什么是JSONJSON的全称是Javascript Object Notation,是一种数据结构化交互的标准协议,非常容易阅读与编写,可以作为配置文件保存应用的相关配置信息,也可以作为前后端接口数据的格式,比如后端返回给前端这样的JSON数据:

{
	"code":404,
	"msg":"Not Found"
}

JSON的数据类型

JSON只支持六种数据类型,分别是:

  • 数字(number):有十进制和科学记数学两种表示方式。
  • 字符串(string):使用双引号表示的Unicode字符序列。
  • 布尔(bool)true或者false
  • 对象(object):使用花括号({})括起来的一个或多个键值对(key/value),用逗号(,)隔开,最后一个键值对后面不能有逗号,键(key)必是双引号("")引起来的字符串,而值则可以是任意类型(如:布尔、数字、对象、数组、字符串)。
  • 数组(array):使用中括号([])括起来的值的集合,这些值可是任意类型(布尔、数字、对象、数组、字符串)。
  • null:空值。

JSON类型与Go数据类型的对应关系:

  • Go的boolean对应JSON的bool类型
  • Go的整型与浮点型对应JSON的number类
  • Go的map和struct对应JSON的object,map的key必须是字符串
  • Go的切片与数组对应JSON的array
  • Go的chan与函数等复杂的类型不能作为JSON的值

JSON的基本操作

Go语言在encoding/json包中提供了JSON数据序列化与反序列化的操作,最简单直接就是调用Marshal把一个Go的数据序列化为一个JSON数据和调用Unmarshal对JSON数据反序列化。

Marshal

json包的Marshal()函数用于将数据编码为一个JSON文本,该函数的签名如下所示:

func Marshal(v interface{}) ([]byte, error) 

Marshal()函数可以接受任意值,并返回JSON化的字节数组与一个error类型:

package main
import (
	"encoding/json"
	"fmt"
)
func main() {
	m := map[string]int{"Go入门": 1, "Go Ation": 2, "Go从入门到精通": 3, "Go高级编程": 4}
	data, err := json.Marshal(m)
	if err != nil {
		panic(err)
	}
    //转换为string并输出
	fmt.Println(string(data)) 
}

上面示例程序的运行结果:

{"Go Ation":2,"Go从入门到精通":3,"Go入门":1,"Go高级编程":4}

MarshalIndent

上面调用Marshal函数序列化的数据输出了一行,如果数据太长,则不利于我们阅读,要想输出的JSON数据格式更友好一点,可以用MarshalIndent函数,该函数签名如下:

func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

相比于Marshal函数,MarshalIndent函数可以接收一个prefixindent,用于设置输出JSON每一行的前缀和首行缩进,下面是一个使用该函数的例子:

package main
import (
	"encoding/json"
	"fmt"
)
func main() {
	m := map[string]int{"Go入门": 1, "Go Ation": 2, "Go从入门到精通": 3, "Go高级编程": 4}
    //设置prefix为空,indent为tab
	data, err := json.MarshalIndent(m, "", "\t")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(data))
}

设置了indent参数后的运行结果:

{
	"Go Ation": 2,
    "Go从入门到精通": 3,
    "Go入门": 1,
    "Go高级编程": 4
}

json.Unmarshal

json包的Unmarshal()函数作用与Marshal刚好相反,用于将一个JSON数据格式反序列化到一个我们定义好的Go类型变量中,该函数的签名如下所示:

func Unmarshal(data []byte, v interface{}) error 

下面是一个反序列化的示例:

package main
import (
	"encoding/json"
	"fmt"
)
type Book struct {
	BookName string `json:"name"`
	Number   int    `json:"num"`
}
func main() {
	jsonStr := `[{"name":"Go入门","num": 1}, {"name":"Go Ation","num": 2}, {"name":"Go从入门到精通","num": 3}, {"name":"Go高级编程","num": 4}]`
	var books []Book
	err := json.Unmarshal([]byte(jsonStr), &books)
	if err != nil {
		panic(err)
	}
	fmt.Println(books)
}

上面的例子中,我们将一个JSON反序化到一个数组当中,而该数组的元素是一个自定义的struct类型,在struct中可以通过json tag来定义json的字段名称。

自定义序列化

如果是自定义的数据类型,我们可以实现Marshaler接口和Unmarshaler接口的方法,这两个接口的定义如下:

type Unmarshaler interface {
	UnmarshalJSON([]byte) error
}
type Marshaler interface {
	MarshalJSON() ([]byte, error)
}

当调用Marshal函数序列化时,会调用MarshalJSON()函数,我们可以在该函数中自定义序列化的逻辑:

package main
import (
	"encoding/json"
	"fmt"
	"strings"
)
type Reason uint8
const (
	Unknown = iota
	Spring
	Summer
	Autumn
	Winter
)
//自定义反序列逻辑
func (r *Reason) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	switch strings.ToLower(s) {
	default:
		*r = Unknown
	case "spring":
		*r = Spring
	case "summer":
		*r = Summer
	case "autumn":
		*r = Autumn
	case "winter":
		*r = Winter
	}
	return nil
}
//自定义序列逻辑
func (r Reason) MarshalJSON() ([]byte, error) {
	var s string
	switch r {
	default:
		s = "unknonw"
	case Spring:
		s = "spring"
	case Summer:
		s = "summer"
	case Autumn:
		s = "autumn"
	case Winter:
		s = "winter"
	}
	return json.Marshal(s)
}
func main() {
	r := []Reason{1, 2, 3, 4}
	data, err := json.Marshal(r)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(data))
}

上面的代码中,Reason也实现了Unmarshaler接口,因此在调用Unmarshal函数反序列化时会调用UnmarshalJSON,在该方法中我们可以自定义反序列化的逻辑:

func main() {
	jsonStr := `["winter","spring","test"]`
	var r []Reason
	err := json.Unmarshal([]byte(jsonStr), &r)
	if err != nil {
		panic(err)
	}
	fmt.Println(r)
} 

Encoder与Decoder

调用Marshal只会返回一个字节数组,Unmarshal函数只能传入一个字节数组,如果我们想将序列化的数据写入到不同的地方(比如保存到一个文件中),或者想从不同地方读取JSON数据(比如从一个文件读取),那么用EncoderDecoder会更方便。

json.Encoder

NewEncoder函数可以创建一个Encoder结构体,NewEncoder函数接收一个实现了io.Writer接口的参数,在序列化后会调用io.Writer写入数据:

package main
import (
	"encoding/json"
	"os"
)
func main() {
	m := map[string]int{"Go入门": 1, "Go Ation": 2, "Go从入门到精通": 3, "Go高级编程": 4}
	file, err := os.Create("config.json")
	if err != nil {
		panic(err)
	}
    defer file.Close()
	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "\t")
	err = encoder.Encode(m)
	if err != nil {
		panic(err)
	}
}

上面程序运行后,会把JSON数据保存到当前目录的config.json文件当中。

json.Decoder

NewDecoder函数可以创建一个Decoder结构体,NewDecoder函数接收一个实现了io.Reader接口的参数,也就是说Decoderio.Reader中读取数据:

package main
import (
	"encoding/json"
	"fmt"
	"os"
)
func main() {
	file, err := os.Open("config.json")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	decoder := json.NewDecoder(file)
	var m map[string]int
	err = decoder.Decode(&m)
	if err != nil {
		panic(err)
	}
	fmt.Println(m)
}

小结

JSON是一种非常通用的数据格式,经常被用于前后端api接口的数据通信,而Go语言在标准库encoding/json包中为JSON数据的编码与反编码提供非常好的支持。

好了,相信看完这篇文章,你应该掌握了以下几点了吧:

  • 什么是JSON
  • 如何序列化与反序列化JSON数据
  • 如何自定义序列化过程
  • json.Encoder与json.Decoder的使用

到此这篇关于重学Go语言之JSON操作详解的文章就介绍到这了,更多相关Go JSON内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言并发之Select多路选择操作符用法详解

    Go语言并发之Select多路选择操作符用法详解

    Go 语言借用多路复用的概念,提供了 select 关键字,用于多路监听多个通道,本文就来和大家聊聊Go语言中Select多路选择操作符的具体用法,希望对大家有所帮助
    2023-06-06
  • GO项目实战之Gorm格式化时间字段实现

    GO项目实战之Gorm格式化时间字段实现

    GORM自带的time.Time类型JSON默认输出RFC3339Nano格式的,下面这篇文章主要给大家介绍了关于GO项目实战之Gorm格式化时间字段实现的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • Go语言中的函数详解

    Go语言中的函数详解

    函数是基本的代码块,用于执行一个任务。本文详细讲解了Go语言中的函数,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Go语言中 Channel 详解

    Go语言中 Channel 详解

    Go 语言中的 channel 是实现 goroutine 间无锁通信的关键机制,他使得写多线程并发程序变得简单、灵活、触手可得。下面就个人理解对 channel 使用过程中应该注意的地方进行一个简要的总结。
    2018-10-10
  • Go语言常见错误接口污染解决分析

    Go语言常见错误接口污染解决分析

    这篇文章主要为大家介绍了Go语言常见错误接口污染解决分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Golang学习笔记(六):struct

    Golang学习笔记(六):struct

    这篇文章主要介绍了Golang学习笔记(六):struct,本文讲解了struct的声明及初始化、struct的匿名字段(继承)、method、method继承和重写等内容,需要的朋友可以参考下
    2015-05-05
  • 使用go语言实现查找两个数组的异同操作

    使用go语言实现查找两个数组的异同操作

    这篇文章主要介绍了使用go语言实现查找两个数组的异同操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang中Map按照Value大小排序的方法实例

    Golang中Map按照Value大小排序的方法实例

    这篇文章主要给大家介绍了关于Golang中Map按照Value大小排序的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-03-03
  • 基于Go语言实现一个并发端口扫描器

    基于Go语言实现一个并发端口扫描器

    这篇文章主要介绍了如何使用 Go 实现一个并发端口扫描器,通过 Goroutine 并发扫描多个端口,极大地提升端口扫描的效率,本文不仅讲解了如何使用 Go 的并发特性,还涉及了如何处理超时和错误,保证端口扫描的健壮性和效率,需要的朋友可以参考下
    2025-08-08
  • Go项目中添加生成时间与版本信息的方法

    Go项目中添加生成时间与版本信息的方法

    本文主要介绍了Go项目中添加生成时间与版本信息的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04

最新评论