简体   繁体   English

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

[英]why json.Unmarshal change the pointer value after append a new one in golang?

I have a question about why json.Unmarshal change the pointer value (create_at) first ( test1 ) after append a new one ( test2 ) but num is dont change in golang ?我有一个问题,为什么 json.Unmarshal 在追加一个新的( test2 )之后首先更改指针值( create_at )test1 )但num在 golang 中没有变化?

json.Unmarshal will reuse the address ? json.Unmarshal 会重用地址吗? I don't understand why append a new value (don't pointer value) will influences the elements insert before, if I change the *time.Time -> time.Time, this problem will be solve...我不明白为什么追加一个新值(不是指针值)会影响之前插入的元素,如果我改变*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()
}

The short version : all elements in your slice are shallow copies of res , so the CreateAt field points to the same value.简短版本:切片中的所有元素都是res浅拷贝,因此CreateAt字段指向相同的值。

Details :详情

When appending res to icr , the element added to icr is a copy of res .res附加到icr ,添加到icr的元素是res的副本。

This works fine for the field Num , it can be modified in res without impacting the data stored in icr .这适用于字段Num ,它可以在res修改而不会影响存储在icr的数据。 This is because it's a base type.这是因为它是一个基本类型。

However, the CreateAt field of res is a pointer , so the copy is still the same pointer.但是, resCreateAt字段是一个指针,因此副本仍然是同一个指针。 All elements of icr will have CreateAt point to the same value. icr所有元素都将具有CreateAt指向相同的值。 Any modification of that value will be reflected in all elements of icr .对该值的任何修改都将反映在icr所有元素中。

You have two options (at least):您有两个选择(至少):

  • change CreateAt to plain time.Time , meaning it will be copied and not just a pointer改变CreateAt平淡time.Time ,这意味着它会被复制,而不是只是一个指针
  • use a different variable to unmarshal the second time.使用不同的变量第二次解组。 eg: json.Unmarshal(newBytes.Bytes(), &res2)例如: json.Unmarshal(newBytes.Bytes(), &res2)

Here is a clearer example without json or slices, just two variables one of which is a copy of the other: see on playground :这是一个没有 json 或 slices 的更清晰的示例,只有两个变量,其中一个是另一个的副本: 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()
}

Output:输出:

{
  "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"
}

When updating res2 , the res2.Num does not impact res.Num since it is a base type.更新res2res2.Num不会影响res.Num因为它是基本类型。 However, res2.CreateAt and res.CreateAt both point to the same object so the change is reflected in both.但是, res2.CreateAtres.CreateAt都指向同一个对象,因此更改会反映在两者中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM