
本文介绍使用 map[string]interface{} 和递归类型断言,高效遍历动态、深度嵌套的 json 数据,无需预先定义 struct,适用于配置解析、日志提取、通用 api 响应处理等场景。
在 Go 中处理未知结构的 JSON(即“任意嵌套 JSON”)时,不能依赖固定 struct,而应采用 encoding/json 包配合 interface{} 的泛型解码方式。核心思路是:将 JSON 解析为 map[string]interface{}(顶层对象)或 []interface{}(数组),再通过类型断言 + 递归逐层展开,统一处理所有键值对。
以下是一个健壮、可复用的递归解析实现:
package main
import (
"encoding/json"
"fmt"
)
func main() {
var data map[string]interface{}
if err := json.Unmarshal([]byte(input), &data); err != nil {
panic(fmt.Sprintf("JSON 解析失败: %v", err))
}
parseMap(data, "")
}
// parseMap 递归遍历 map[string]interface{},prefix 用于打印缩进(可选)
func parseMap(m map[string]interface{}, prefix string) {
for key, val := range m {
fullKey := key
if prefix != "" {
fullKey = prefix + "." + key
}
switch v := val.(type) {
case map[string]interface{}:
fmt.Printf("%s → (object)\n", fullKey)
parseMap(v, fullKey)
case []interface{}:
fmt.Printf("%s → (array, len=%d)\n", fullKey, len(v))
parseArray(v, fullKey)
case string, int, int64, float64, bool, nil:
fmt.Printf("%s = %v (type: %T)\n", fullKey, v, v)
default:
fmt.Printf("%s = %v (unknown type: %T)\n", fullKey, v, v)
}
}
}
// parseArray 递归遍历 []interface{},支持嵌套对象/数组
func parseArray(a []interface{}, prefix string) {
for i, item := range a {
indexKey := fmt.Sprintf("%s[%d]", prefix, i)
switch v := item.(type) {
case map[string]interface{}:
fmt.Printf("%s → (object)\n", indexKey)
parseMap(v, indexKey)
case []interface{}:
fmt.Printf("%s → (array, len=%d)\n", indexKey, len(v))
parseArray(v, indexKey)
case string, int, int64, float64, bool, nil:
fmt.Printf("%s = %v (type: %T)\n", indexKey, v, v)
default:
fmt.Printf("%s = %v (unknown type: %T)\n", indexKey, 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{}) 显式断言,否则无法调用 .range 或访问字段。
- 递归终止条件自然:当值为基本类型(string/int/bool/nil)或非容器类型时,直接输出;遇到 map 或 slice 则继续递归。
- 支持任意深度:无论 InnerInnerJSONArray 嵌套几层(如 [{"a": [{"b": true}]}]),该方案均可正确展开。
-
生产建议:
- 若需提取特定字段(如所有 "fld1"),可在 parseMap 中添加匹配逻辑(如 if key == "fld1" { ... });
- 对性能敏感场景,可结合 json.RawMessage 延迟解析;
- 避免无限递归:确保输入 JSON 合法(无循环引用,Go 的 json 包本身已拒绝此类输入)。
该方法是 Go 处理动态 JSON 的标准实践,兼顾简洁性、可读性与工程鲁棒性,特别适合初学者快速上手并构建可维护的 JSON 工具链。










