在 Go 语言中,json.Marshal和json.Unmarshal是用于处理 JSON 数据的两个关键函数。
它们的使用涉及到传递指针或非指针类型的细微差别。
json.Marshal的序列化
json.Marshal函数将 Go 语言的值序列化为 JSON 字符串。在使用json.Marshal时,通常传递非指针类型即可。
- 传值类型: 当你传递一个结构体、切片、映射、数组等类型时,json.Marshal 会自动将这些值序列化为 JSON。 示例:
type Person struct {
Name string
Age int
}
person := Person{Name: "Alice", Age: 30}
jsonData, err := json.Marshal(person) // 传递值类型
if err != nil {
log.Fatal(err)
}
fmt.Println(string(jsonData))
// 输出: {"Name":"Alice","Age":30}
- 传指针类型: 虽然可以传递指针类型给json.Marshal,但这不是必需的,因为 Go 会自动处理指针的解引用。也就是说,无论是值还是指针,结果都是相同的。 示例:
personPtr := &Person{Name: "Alice", Age: 30}
jsonData, err := json.Marshal(personPtr) // 传递指针类型
if err != nil {
log.Fatal(err)
}
fmt.Println(string(jsonData))
// 输出: {"Name":"Alice","Age":30}
json.Unmarshal的反序列化
json.Unmarshal函数用于将 JSON 字符串解析为 Go 语言的值。在使用 json.Unmarshal时,必须传递指针类型,以便函数能够修改目标对象。
- 传指针类型: 因为json.Unmarshal需要将解析后的数据写入目标变量中,所以需要传递目标变量的指针。只有传递指针,json.Unmarshal才能修改目标变量的值。 示例:
jsonData := `{"Name":"Alice","Age":30}`
var person Person
err := json.Unmarshal([]byte(jsonData), &person) // 传递指针类型
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", person)
// 输出: {Name:Alice Age:30}
- 不传指针会报错: 如果你不传递指针,而是传递一个值,json.Unmarshal将无法修改目标值,并且会报错。 示例:
var person Person
err := json.Unmarshal([]byte(jsonData), person) // 不传递指针会报错
if err != nil {
log.Fatal(err) // 会在这里报错:json: Unmarshal(non-pointer main.Person)
}
json的序列化应用
这里我们主要介绍一下结构体、map 和切片的序列化,其它数据类型的序列化与其类似。
package main
import (
"fmt"
"encoding/json"
)
//定义一个结构体
type Monster struct {
Name string
Age int
Birthday string
Sal float64
Skill string
}
func testStruct() {
monster := Monster{
Name :"牛魔王", Age : 500,
Birthday : "2011-11-11",
Sal : 8000.0,
Skill : "牛魔拳",
}
//将 monster 序列化
data, err := json.Marshal(&monster)
if err != nil {
fmt.Printf("序列号错误 err=%v\n", err)
}
//输出序列化后的结果
fmt.Printf("monster 序列化后=%v\n", string(data))
}
//将 map 进行序列化
func testMap() {
//定义一个 map
var a map[string]interface{}
//使用 map,需要 make
a = make(map[string]interface{})
a["name"] = "红孩儿"
a["age"] = 30
a["address"] = "洪崖洞"
//将 a 这个 map 进行序列化
//将 monster 序列化
data, err := json.Marshal(a)
if err != nil {
fmt.Printf("序列化错误 err=%v\n", err)
}
//输出序列化后的结果
fmt.Printf("a map 序列化后=%v\n", string(data))
}
//对切片进行序列化, 我们这个切片 []map[string]interface{}
func testSlice() {
var slice []map[string]interface{}
var m1 map[string]interface{}
//使用 map 前,需要先 make
m1 = make(map[string]interface{})
m1["name"] = "jack"
m1["age"] = "7"
m1["address"] = "北京"
slice = append(slice, m1)
var m2 map[string]interface{}
//使用 map 前,需要先 make
m2 = make(map[string]interface{})
m2["name"] = "tom"
m2["age"] = "20"
m2["address"] = [2]string{"墨西哥","夏威夷"}
slice = append(slice, m2)
//将切片进行序列化操作
data, err := json.Marshal(slice)
if err != nil {
fmt.Printf("序列化错误 err=%v\n", err)
}
//输出序列化后的结果
fmt.Printf("slice 序列化后=%v\n", string(data))
}
//对基本数据类型序列化,对基本数据类型进行序列化意义不大
func testFloat64() {
var num1 float64 = 2345.67
//对 num1 进行序列化
data, err := json.Marshal(num1)
if err != nil {
fmt.Printf("序列化错误 err=%v\n", err)
}
//输出序列化后的结果
fmt.Printf("num1 序列化后=%v\n", string(data))
}
func main() {
//演示将结构体, map , 切片进行序列号
testStruct()
testMap()
testSlice()//演示对切片的序列化
testFloat64()//演示对基本数据类型的序列化
}
注意事项:对于结构体的序列化,如果我们希望序列化后的key 的名字,又我们自己重新制定,那么可以给struct指定一个 tag 标签。
json的反序列化应用
这里我们主要介绍一下将 json 字符串反序列化成结构体、map 和切片,其它数据类型的反序列化与其类似。
package main
import (
"fmt"
"encoding/json"
)
//定义一个结构体
type Monster struct {
Name string
Age int
Birthday string
Sal float64
Skill string
}
//演示将 json 字符串,反序列化成 struct
func unmarshalStruct() {
//说明 str 在项目开发中,是通过网络传输获取到.. 或者是读取文件获取到
str := "{\"Name\":\"牛魔王\",\"Age\":500,\"Birthday\":\"2011-11-11\",\"Sal\":8000,\"Skill\":\"牛魔拳\"}"
//定义一个 Monster 实例
var monster Monster
err := json.Unmarshal([]byte(str), &monster)
if err != nil {
fmt.Printf("unmarshal err=%v\n", err)
}
fmt.Printf("反序列化后 monster=%v monster.Name=%v \n", monster, monster.Name)
}
//将 json 字符串,反序列化成 map
func unmarshalMap() {
str := "{\"address\":\"洪崖洞\",\"age\":30,\"name\":\"红孩儿\"}"
//定义一个 map
var a map[string]interface{}
//反序列化
//注意:反序列化 map,不需要 make,因为 make 操作被封装到 Unmarshal 函数
err := json.Unmarshal([]byte(str), &a)
if err != nil {
fmt.Printf("unmarshal err=%v\n", err)
}
fmt.Printf("反序列化后 a=%v\n", a)
}
//将 json 字符串,反序列化成切片
func unmarshalSlice() {
str := "[{\"address\":\"北京\",\"age\":\"7\",\"name\":\"jack\"}," +
"{\"address\":[\"墨西哥\",\"夏威夷\"],\"age\":\"20\",\"name\":\"tom\"}]"
//定义一个 slice
var slice []map[string]interface{}
//反序列化,不需要 make,因为 make 操作被封装到 Unmarshal 函数
err := json.Unmarshal([]byte(str), &slice)
if err != nil {
fmt.Printf("unmarshal err=%v\n", err)
}
fmt.Printf("反序列化后 slice=%v\n", slice)
}
func main() {
unmarshalStruct()
unmarshalMap()
unmarshalSlice()
}
注意事项:
-
在反序列化一个json 字符串时,要确保反序列化后的数据类型和原来序列化前的数据类型一致。
-
如果 json 字符串是通过程序获取到的,则不需要再对 转义处理。
总结
- json.Marshal:既可以传递值也可以传递指针,一般情况下传递值即可。
- json.Unmarshal: 必须传递指针,以便函数能够修改目标变量。