问题描述
我有一个关于为什么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.Num
和res2.CreateAt
都指向同一个对象,因此更改会同时反映在两者中。