重学Go语言之JSON操作详解
作者:程序员读书
当你想在项目中保存配置时,你会选择什么数据格式,你可能会说用yaml
或是json
,当你作为后端要把数据返回给前端时,你会选择什么数据格式,这次我想,一定是json
了吧。
关于在Go语言如何操作JSON
,在这篇文章我们来探究一下!
JSON
什么是JSON
?JSON
的全称是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
函数可以接收一个prefix
和indent
,用于设置输出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数据(比如从一个文件读取),那么用Encoder
和Decoder
会更方便。
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
接口的参数,也就是说Decoder
从io.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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!