• 作者:老汪软件技巧
  • 发表时间: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 是引用类型,在需要改变其长度、容量或指向的底层数组时,仍然需要使用指针。


上一条查看详情 +Camunda 抄送功能实现
下一条 查看详情 +没有了