
本文介绍使用 `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: %s\n", key)
parseMap(v) // 递归进入下一层对象
case []interface{}:
fmt.Printf("→ Array: %s\n", 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] → Object\n", i)
parseMap(v)
case []interface{}:
fmt.Printf(" [Index %d] → Array\n", 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 响应泛化处理等场景。










