
本文介绍使用 `map[String]interface{}` 和递归类型断言,高效遍历动态、多层嵌套的 json 数据,自动识别并处理对象(map)、数组(slice)和基础值,适用于无法提前定义 Struct 的场景。
在 go 中处理未知结构的 jsON(即“任意嵌套 json”)时,最常用且推荐的方式是将 JSON 解码为 map[string]Interface{}(对应 JSON 对象)和 []interface{}(对应 JSON 数组)。由于 Go 是静态类型语言,interface{} 本身不携带运行时类型信息,因此必须通过类型断言(type assertion) 或 类型开关(type switch) 显式判断实际类型,才能安全访问其内容。
下面是一个完整、可直接运行的递归解析方案:
package main import ( "encoding/json" "fmt" ) func main() { var m map[string]interface{} if err := json.Unmarshal([]byte(input), &m); err != nil { panic(err) } parseMap(m) } // parseMap 递归遍历 JSON 对象(map[string]interface{}) func parseMap(m map[string]interface{}) { for key, val := range m { switch v := val.(type) { case map[string]interface{}: fmt.printf("→ Object: %sn", key) parseMap(v) // 递归进入下一层对象 case []interface{}: fmt.Printf("→ Array: %sn", key) parseArray(v) // 递归进入数组元素 default: // 基础类型:string, number (Float64), bool, nil fmt.Printf(" %s: %v (type: %T)n", key, v, v) } } } // parseArray 递归遍历 JSON 数组([]interface{}) func parseArray(a []interface{}) { for i, val := range a { switch v := val.(case) { case map[string]interface{}: fmt.Printf(" [Index %d] → Objectn", i) parseMap(v) case []interface{}: fmt.Printf(" [Index %d] → Arrayn", i) parseArray(v) default: fmt.Printf(" [Index %d]: %v (type: %T)n", i, v, v) } } } const input = ` { "outterJSON": { "innerJSON1": { "value1": 10, "value2": 22, "InnerInnerArray": ["test1", "test2"], "InnerInnerJSONArray": [{"fld1": "val1"}, {"fld2": "val2"}] }, "InnerJSON2": "NoneValue" } }`
✅ 关键要点说明:
- 类型安全是前提:interface{} 必须用 val.(type) 类型开关或 val.(map[string]interface{}) 断言明确转换,否则编译失败或 panic;
- 递归终止条件自然存在:当遇到 string/float64/bool/nil 等非复合类型时,递归停止,直接输出;
- 数组索引清晰可见:parseArray 显式打印 Index N,便于定位元素位置(例如 “fld1” 位于 InnerInnerJSONArray[0]);
- 类型保留准确:fmt.Printf(“%T”, v) 可确认数字默认为 float64(JSON 规范无 int/float 区分),如需整数可做 int(v.(float64)) 转换(注意精度与边界检查);
- 性能足够高效:无反射滥用,仅用原生类型判断,适合中等规模 JSON(GB 级建议改用 json.Decoder 流式解析)。
⚠️ 注意事项:
- 若 JSON 中含 NULL,对应值为 nil(v == nil),需单独处理;
- 不支持 JSON 中的 number 溢出或超长小数(Go float64 精度限制);
- 如需提取所有 fld1 字段值,可在 parseMap 中增加匹配逻辑(例如 if key == “fld1” { fmt.Println(“Found fld1 =”, v) }),无需修改主干结构。
该方案是 Go 处理动态 JSON 的标准实践,简洁、健壮、易于扩展,特别适合配置解析、日志分析、API 响应泛化处理等场景。