为什么在golang中添加一个新的json.Unmarshal后更改指针值?

问题描述

我有一个关于为什么json的问题。解组追加新的( test2 后先( test1 )先取消更改指针值(create_at) >),但是golang中的 num 不变吗?

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(),//     "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中的数据。这是因为它是基本类型。

但是,CreateAt的{​​{1}}字段是一个指针,因此副本仍是相同的指针。 res的所有元素都将icr指向相同的值。该值的任何修改都将反映在CreateAt的所有元素中。

您有两个选择(至少):

  • icr更改为普通CreateAt,这意味着它将被复制而不仅仅是指针
  • 使用其他变量进行第二次解组。例如:time.Time

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

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

输出:

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.Numres2.CreateAt都指向同一个对象,因此更改会同时反映在两者中。