簡體   English   中英

為什么 json.Unmarshal 在 golang 中追加一個新的指針后改變指針值?

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

我有一個問題,為什么 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 或 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()
}

輸出:

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

更新res2res2.Num不會影響res.Num因為它是基本類型。 但是, res2.CreateAtres.CreateAt都指向同一個對象,因此更改會反映在兩者中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM