Go语言解析JSON生成指定层级Map的错误排查求助
Go分组JSON数据时Map结构不符合预期的问题排查与修复
问题描述
原始JSON结构
{ "items": [ {"pkg": "0", "pin": "0", "item": "0"}, {"pkg": "1", "pin": "1", "item": "1"}, {"pkg": "0", "pin": "2", "item": "2"}, {"pkg": "1", "pin": "3", "item": "3"}, {"pkg": "0", "pin": "4", "item": "4"}, {"pkg": "1", "pin": "5", "item": "5"}, {"pkg": "0", "pin": "6", "item": "6"}, {"pkg": "1", "pin": "7", "item": "7"}, {"pkg": "0", "pin": "8", "item": "8"}, {"pkg": "1", "pin": "9", "item": "9"}, {"pkg": "0", "pin": "10", "item": "10"}, {"pkg": "1", "pin": "11", "item": "11"}, {"pkg": "0", "pin": "12", "item": "12"}, {"pkg": "1", "pin": "13", "item": "13"}, {"pkg": "0", "pin": "14", "item": "14"}, {"pkg": "1", "pin": "15", "item": "15"}, {"pkg": "0", "pin": "16", "item": "16"}, {"pkg": "1", "pin": "17", "item": "17"}, {"pkg": "0", "pin": "18", "item": "18"}, {"pkg": "1", "pin": "19", "item": "19"}, {"pkg": "0", "pin": "20", "item": "20"}, {"pkg": "1", "pin": "21", "item": "21"}, {"pkg": "0", "pin": "22", "item": "22"}, {"pkg": "1", "pin": "23", "item": "23"}, {"pkg": "0", "pin": "24", "item": "24"}, {"pkg": "1", "pin": "25", "item": "25"}, {"pkg": "0", "pin": "26", "item": "26"}, {"pkg": "1", "pin": "27", "item": "27"}, {"pkg": "0", "pin": "28", "item": "28"}, {"pkg": "1", "pin": "29", "item": "29"}, {"pkg": "0", "pin": "30", "item": "30"}, {"pkg": "1", "pin": "31", "item": "31"}, {"pkg": "0", "pin": "0", "item": "32"}, {"pkg": "1", "pin": "1", "item": "33"}, {"pkg": "0", "pin": "2", "item": "34"}, {"pkg": "1", "pin": "3", "item": "35"}, {"pkg": "0", "pin": "4", "item": "36"}, {"pkg": "1", "pin": "5", "item": "37"}, {"pkg": "0", "pin": "6", "item": "38"}, {"pkg": "1", "pin": "7", "item": "39"}, {"pkg": "0", "pin": "8", "item": "40"}, {"pkg": "1", "pin": "9", "item": "41"} ] }
用于解析的Go代码
package basics import ( "encoding/json" "fmt" "io/ioutil" "strconv" ) type NNodes struct { Items []NodeItem `json:"items"` } type NodeItem struct { Pkg string `json:"pkg"` Pin string `json:"pin"` Item string `json:"item"` } func parser() { data, _ := ioutil.ReadFile("list.json") var result NNodes var pkg, pin, item int json.Unmarshal(data, &result) foobar := make(map[int]map[int][]int) siblings := make(map[int][]int) for _, value := range result.items { if pkg, err = strconv.Atoi(value.Pkg); err != nil { break } if pin, err = strconv.Atoi(value.Pin); err != nil { break } if item, err = strconv.Atoi(value.Item); err != nil { break } siblings[pin] = append(siblings[pin], item) foobar[pkg] = siblings } fmt.Println(foobar) }
当前输出
map[0:map[0:[0 32] 1:[1 33] 2:[2 34] 3:[3 35] 4:[4 36] 5:[5 37] 6:[6 38] 7:[7 39] 8:[8 40] 9:[9 41] 10:[10] 11:[11] 12:[12] 13:[13] 14:[14] ... ]
期望输出
map[0:map[0:[0,32] 2:[2,34] 4:[4,36] 6:[6,38]... ] map[1:map[1:[1,33] 3:[3,35] 5:[5,37] 7:[7,39]... ]
错误原因
核心问题是所有pkg共用了同一个siblings map。在Go中,map是引用类型,每次执行foobar[pkg] = siblings时,都是把同一个map的地址赋值给不同的pkg键,导致所有pkg对应的子map都是同一个实例,最终所有pin的数据都混在一起。
另外还有两个次要问题:
- 错误处理过于粗暴:只要有一个转换失败就直接break,应该记录错误并跳过当前条目,而不是终止整个循环
- 结构体字段名:
result.items应该是result.Items(结构体里定义的是首字母大写的Items,Go的JSON解析依赖导出字段)
修正后的代码
package basics import ( "encoding/json" "fmt" "io/ioutil" "strconv" ) type NNodes struct { Items []NodeItem `json:"items"` } type NodeItem struct { Pkg string `json:"pkg"` Pin string `json:"pin"` Item string `json:"item"` } func parser() { data, err := ioutil.ReadFile("list.json") if err != nil { fmt.Printf("read file error: %v\n", err) return } var result NNodes err = json.Unmarshal(data, &result) if err != nil { fmt.Printf("unmarshal json error: %v\n", err) return } foobar := make(map[int]map[int][]int) for _, value := range result.Items { // 修正字段名:Items而非items pkg, err := strconv.Atoi(value.Pkg) if err != nil { fmt.Printf("invalid pkg: %s, error: %v\n", value.Pkg, err) continue // 转换失败时跳过当前条目,而非终止循环 } pin, err := strconv.Atoi(value.Pin) if err != nil { fmt.Printf("invalid pin: %s, error: %v\n", value.Pin, err) continue } item, err := strconv.Atoi(value.Item) if err != nil { fmt.Printf("invalid item: %s, error: %v\n", value.Item, err) continue } // 检查当前pkg是否已有对应的子map,没有则创建新的 if _, ok := foobar[pkg]; !ok { foobar[pkg] = make(map[int][]int) } // 向当前pkg的子map中添加item foobar[pkg][pin] = append(foobar[pkg][pin], item) } fmt.Println(foobar) }
关键修复点
- 移除全局的
siblingsmap,改为针对每个pkg动态创建子map:处理某个pkg时,先检查foobar中是否已存在该pkg对应的子map,不存在则初始化一个新的 - 修正结构体字段访问:使用
result.Items(首字母大写,确保字段可被JSON解析) - 优化错误处理:转换失败时跳过当前条目,而非直接终止循环,同时添加错误日志便于调试
- 避免引用类型共享问题,每个pkg拥有独立的子map,数据不会交叉污染
内容的提问来源于stack exchange,提问作者Niranjan M.R
相关产品推荐
相关产品推荐

