登录
首页 >  Golang >  Go问答

为什么在 Golang 中对指针进行 JSON 反序列化后指针值会被修改?

来源:stackoverflow

时间:2024-02-18 14:36:27 211浏览 收藏

积累知识,胜过积蓄金银!毕竟在Golang开发的过程中,会遇到各种各样的问题,往往都是一些细节知识点还没有掌握好而导致的,因此基础知识点的积累是很重要的。下面本文《为什么在 Golang 中对指针进行 JSON 反序列化后指针值会被修改?》,就带大家讲解一下知识点,若是你对本文感兴趣,或者是想搞懂其中某个知识点,就请你继续往下看吧~

问题内容

我有一个问题,为什么 json.unmarshal 在附加一个新指针值 (test2) 之后首先更改指针值 (create_at) (test1) >) 但是 num 在 golang 中没有改变?

json.unmarshal 会重用该地址吗?我不明白为什么追加一个新值(不要指针值)会影响之前插入的元素,如果我更改 *time.time -> time.time,这个问题就会解决...

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "time"
)

type test struct {
    Num      int        `json:"num"`
    CreateAt *time.Time `json:"create_at"`
}

func main() {

    var icr []test
    var res test

    now := time.Now()
    next := time.Now().Add(time.Hour)

    test1 := test{
        Num:      1,
        CreateAt: &now,
    }

    test2 := test{
        Num:      2,
        CreateAt: &next,
    }

    newBytes := new(bytes.Buffer)
    json.NewEncoder(newBytes).Encode(test1)

    json.Unmarshal(newBytes.Bytes(), &res)

    icr = append(icr, res)

    fmt.Println(PrettyPrint(icr))
    // [
    //   {
    //     "num": 1,
    //     "create_at": "2020-09-24T15:03:00.755169076+08:00"
    //   }
    // ]

    newBytes = new(bytes.Buffer)
    json.NewEncoder(newBytes).Encode(test2)

    json.Unmarshal(newBytes.Bytes(), &res)

    icr = append(icr, res)

    fmt.Println(PrettyPrint(icr))
    // [
    //   {
    //     "num": 1,
    //     "create_at": "2020-09-24T16:03:00.755169556+08:00"
    //   },
    //   {
    //     "num": 2,
    //     "create_at": "2020-09-24T16:03:00.755169556+08:00"
    //   }
    // ]
}

// PrettyPrint ...
func PrettyPrint(data interface{}) string {
    var out bytes.Buffer
    b, _ := json.Marshal(data)
    json.Indent(&out, b, "", "  ")
    return out.String()
}

解决方案


简短版本:切片中的所有元素都是 res 的浅拷贝,因此 createat 字段指向相同的值。

详细信息

res 追加到 icr 时,添加到 icr 的元素是 res 的副本。

这对于字段 num 来说效果很好,可以在 res 中修改它,而不会影响 icr 中存储的数据。这是因为它是基本类型。

但是,rescreateat 字段是一个指针,因此副本仍然是同一个指针。 icr 的所有元素将使 createat 指向相同的值。对该值的任何修改都将反映在 icr 的所有元素中。

你有两个选择(至少):

  • createat 更改为普通 time.time,这意味着它将被复制而不仅仅是一个指针
  • 第二次使用不同的变量进行解组。例如:json.unmarshal(newbytes.bytes(), &res2)

这是一个更清晰的示例,没有 json 或切片,只有两个变量,其中一个是另一个的副本:see on playground

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "time"
)

type test struct {
    num      int        `json:"num"`
    createat *time.time `json:"create_at"`
}

func main() {
    now := time.now()
    res := test{1, &now}
    res2 := res

    fmt.println(prettyprint(res), prettyprint(res2))
    
    // modify res2:
    res2.num = 2
    *res2.createat = time.now().add(time.hour)
    
    fmt.println(prettyprint(res), prettyprint(res2))
}

// prettyprint ...
func prettyprint(data interface{}) string {
    var out bytes.buffer
    b, _ := json.marshal(data)
    json.indent(&out, b, "", "  ")
    return out.string()
}

输出:

{
  "num": 1,
  "create_at": "2009-11-10T23:00:00Z"
} {
  "num": 1,
  "create_at": "2009-11-10T23:00:00Z"
}
{
  "num": 1,
  "create_at": "2009-11-11T00:00:00Z"
} {
  "num": 2,
  "create_at": "2009-11-11T00:00:00Z"
}

更新 res2 时,res2.num 不会影响 res.num,因为它是基本类型。但是,res2.createatres.createat 都指向同一个对象,因此更改会反映在两者中。

本篇关于《为什么在 Golang 中对指针进行 JSON 反序列化后指针值会被修改?》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于Golang的相关知识,请关注golang学习网公众号!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>