- 作者:老汪软件技巧
- 发表时间:2024-08-27 21:03
- 浏览量:
在 Go 语言中,json.Marshal 和 json.Unmarshal 是用于处理 JSON 数据的两个关键函数。
它们的使用涉及到传递指针或非指针类型的细微差别。
json.Marshal
json.Marshal 函数将 Go 语言的值序列化为 JSON 字符串。在使用 json.Marshal 时,通常传递非指针类型即可。
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}
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 时,必须传递指针类型,以便函数能够修改目标对象。
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}
var person Person
err := json.Unmarshal([]byte(jsonData), person) // 不传递指针会报错
if err != nil {
log.Fatal(err) // 会在这里报错:json: Unmarshal(non-pointer main.Person)
}
总结问题 1:slice本身就是引用类型,进行反序列化的时候,是不是可以不用传指针呢?
在 Go 中,虽然 slice 是引用类型,但在反序列化时,仍然需要传递指针给 json.Unmarshal。
Slice 的底层结构
首先,让我们简单回顾一下 Go 中 slice 的底层结构:
type slice struct {
ptr *ElementType // 指向底层数组的指针
len int // 长度
cap int // 容量
}
当你创建一个 slice 时,实际上是在栈上创建了一个包含 ptr、len 和 cap 的结构体。ptr 指向的底层数组存储在堆上。
当你传递 slice 给一个函数时
为什么需要传指针改变 slice 的长度和容量: json.Unmarshal 在解码 JSON 数据时,可能需要调整 slice 的长度和容量。要做到这一点,需要修改 slice 结构体中的 len 和 cap 字段。如果直接传递 slice,而不是它的指针,Unmarshal 操作无法改变 slice 的长度和容量,只能修改它引用的底层数组的数据,而不能扩展这个数组。创建新的底层数组: 如果 JSON 数据的长度超过了 slice 的当前容量,Unmarshal 可能需要分配一个新的底层数组,并更新 slice 的 ptr 字段。为了做到这一点,Unmarshal 需要一个指向 slice 结构体的指针,以便直接修改 ptr 字段。
上述两个要求,对于拷贝 slice 来说,都是做不到的。
反序列化示例代码
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 示例 JSON 数据
jsonData := `[1, 2, 3, 4, 5]`
// 如果你不传递指针,会发生什么?
var data []int
err := json.Unmarshal([]byte(jsonData), data) // 传递 data(非指针)
if err != nil {
fmt.Println("Error:", err) // 会报错:cannot unmarshal array into Go value of type []int
}
// 正确的用法,传递指针
err = json.Unmarshal([]byte(jsonData), &data) // 传递 &data(指针)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Data:", data) // 正常解码出 [1, 2, 3, 4, 5]
}
}
关键点总结
所以,即使 slice 是引用类型,在需要改变其长度、容量或指向的底层数组时,仍然需要使用指针。