在Go语言开发中,我们常常需要解析来自HTTP接口或配置文件的JSON数据。当JSON存在多层嵌套时,标准库encoding/json会将其解析为map[string]interface{}与[]interface{}的组合,数值默认变成float64。要遍历这种结构并准确取出数值,必须配合类型断言。

解析JSON到通用结构
先用json.Unmarshal把字节流解析为interface{},再根据实际类型向下断言。
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := []byte(`{"a":1,"b":{"c":2.5,"d":[3,4]}}`)
var raw interface{}
if err := json.Unmarshal(data, &raw); err != nil {
panic(err)
}
Walk(raw, "")
}
递归遍历与数值断言
通过判断类型分支,遇到map和slice继续递归,遇到数值则做类型断言。注意JSON数字在Go中通常是float64,若原值是整数也可断言为float64再转int。
func Walk(v interface{}, path string) {
switch val := v.(type) {
case map[string]interface{}:
for k, item := range val {
Walk(item, path+"."+k)
}
case []interface{}:
for i, item := range val {
Walk(item, fmt.Sprintf("%s[%d]", path, i))
}
case float64:
// 数值类型断言:JSON数字解析后为float64
fmt.Printf("%s = %v (float64)n", path, val)
intVal := int(val)
fmt.Printf(" 转为int: %dn", intVal)
case string:
fmt.Printf("%s = %s (string)n", path, val)
case nil:
fmt.Printf("%s = niln", path)
default:
fmt.Printf("%s = %v (other)n", path, val)
}
}
常见注意事项
- 如果JSON来自可信源且包含大整数,float64可能丢失精度,可改用json.Decoder并设置UseNumber(),此时数值为json.Number,再用字符串转换。
- 类型断言前尽量使用comma ok形式避免panic,例如
f, ok := val.(float64)。 - 嵌套层级很深时,递归函数要注意栈溢出风险,一般业务JSON不会过深。
使用json.Number处理大数
下面示例展示如何以json.Number方式解析,再做数值断言。
func WalkNumber(v interface{}, path string) {
switch val := v.(type) {
case map[string]interface{}:
for k, item := range val {
WalkNumber(item, path+"."+k)
}
case []interface{}:
for i, item := range val {
WalkNumber(item, fmt.Sprintf("%s[%d]", path, i))
}
case json.Number:
// 此时可安全转为int64或float64
if i, err := val.Int64(); err == nil {
fmt.Printf("%s = %d (int64)n", path, i)
} else if f, err := val.Float64(); err == nil {
fmt.Printf("%s = %v (float64)n", path, f)
}
case string:
fmt.Printf("%s = %s (string)n", path, val)
}
}
通过上述方式,你可以稳定地遍历任意嵌套的JSON结构体,并在遍历过程中对数值做安全的类型断言,提升Go语言数据处理代码的可靠性。