Go语言append添加第二个元素时首个元素被修改的问题排查
Go JSON解析切片引用问题分析与解决
问题原因
你在循环外部仅声明了一次buf []Human切片,而Go语言中切片是引用类型,它本身只存储底层数组的指针、长度和容量。第一次json.Unmarshal将数据解析到buf的底层数组后,你把buf添加到people切片中,此时people的第一个元素是buf的引用(指向同一个底层数组)。
第二次执行json.Unmarshal时,由于buf的底层数组容量足够容纳新数据,Unmarshal会直接覆盖底层数组里的原有内容,这就导致people中已存在的第一个元素(因为是引用)指向的内容也被修改,最终people里的两个元素都指向同一个底层数组,内容完全一致。
解决方法
有两种常用的解决思路:
方法1:每次循环重新声明buf
将var buf []Human移到循环内部,每次迭代都创建一个新的切片,这样每次解析的结果会存储在独立的底层数组中,避免引用复用:
package main import ( "encoding/json" "fmt" ) type Human struct { N string `json:"n"` C string `json:"c"` } func funcname(jDL ...[]byte) { jDL_len := len(jDL) people := [][]Human{} var err error for i := 0; i < jDL_len; i++ { var buf []Human // 每次循环创建新切片 err = json.Unmarshal(jDL[i], &buf) fmt.Println("buf", i, buf) people = append(people, buf) fmt.Println("people", i, people) } jsoned, err := json.Marshal(people) fmt.Println("jsoned", string(jsoned)) fmt.Println("err", err) } func main() { i0 := []byte(`[ { "n": "Alice", "c": "A1" }, { "n": "Bob", "c": "A2" } ]`) i1 := []byte(`[ { "n": "Clark", "c": "B1" }, { "n": "Davis", "c": "B2" } ]`) funcname(i0, i1) }
方法2:复制buf内容后再append
如果需要保留循环外的buf声明,可以在append前创建buf的副本,确保添加到people的是独立的切片:
修改循环内的append语句为:
people = append(people, append([]Human(nil), buf...))
验证结果
修改后运行代码,输出会符合预期:
buf 0 [{Alice A1} {Bob A2}] people 0 [[{Alice A1} {Bob A2}]] buf 1 [{Clark B1} {Davis B2}] people 1 [[{Alice A1} {Bob A2}] [{Clark B1} {Davis B2}]] jsoned [[{"n":"Alice","c":"A1"},{"n":"Bob","c":"A2"}],[{"n":"Clark","c":"B1"},{"n":"Davis","c":"B2"}]] err <nil>
内容的提问来源于stack exchange,提问作者max
相关产品推荐
相关产品推荐

