为什么 json.Unmarshal 在 golang 中追加一个新的指针值后更改指针值?
why json.Unmarshal change the pointer value after append a new one in golang?
我有一个问题,为什么json.Unmarshal先改变指针值(create_at) (test1 ) 添加一个新的后 (test2) 但 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
中的数据。这是因为它是基本类型。
但是res
的CreateAt
字段是一个指针,所以拷贝的还是同一个指针。 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.CreateAt
和 res.CreateAt
都指向同一个对象,因此更改会反映在两者中。
我有一个问题,为什么json.Unmarshal先改变指针值(create_at) (test1 ) 添加一个新的后 (test2) 但 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
中的数据。这是因为它是基本类型。
但是res
的CreateAt
字段是一个指针,所以拷贝的还是同一个指针。 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.CreateAt
和 res.CreateAt
都指向同一个对象,因此更改会反映在两者中。