
本文介绍如何使用 map[String]interface{} 和类型断言,配合递归函数,高效、安全地遍历动态结构的多层嵌套 json,无需预定义 Struct,适用于配置解析、日志分析等场景。
在 go 中处理未知结构的 jsON(即“任意嵌套 json”)时,无法依赖固定 struct,而应采用 encoding/json 包提供的通用解码方式:将 JSON 解析为 map[string]Interface{}(顶层对象)和 []interface{}(数组),再通过类型断言(Type Assertion) 和 类型开关(Type switch) 识别并递归处理每种数据类型。
核心思路是:对每个值进行运行时类型判断——若为 map[string]interface{},则递归遍历其键值对;若为 []interface{},则递归遍历其元素;其余基础类型(如 string、float64、bool、nil)直接处理。这种模式天然支持任意深度嵌套,且代码简洁可维护。
以下为完整、健壮的实现示例:
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 递归解析 map[string]interface{},prefix 用于打印缩进(可选) func parseMap(m map[string]interface{}, prefix string) { for key, val := range m { 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, %d items)n", fullKey, len(v)) parseArray(v, fullKey+".") case string: fmt.Printf("%s: %qn", fullKey, v) case float64: // JSON 数字默认为 float64 fmt.Printf("%s: %gn", fullKey, v) case bool: fmt.Printf("%s: %tn", fullKey, v) case nil: fmt.Printf("%s: nulln", fullKey) default: fmt.Printf("%s: %v (type %T)n", fullKey, v, v) } } } // parseArray 递归解析 []interface{} func parseArray(a []interface{}, prefix string) { for i, val := range a { indexKey := fmt.Sprintf("%s[%d]", prefix, i) switch v := val.(type) { case map[string]interface{}: fmt.Printf("%s: (object)n", indexKey) parseMap(v, indexKey+".") case []interface{}: fmt.Printf("%s: (array, %d items)n", indexKey, len(v)) parseArray(v, indexKey+".") case string: fmt.Printf("%s: %qn", indexKey, v) case float64: fmt.Printf("%s: %gn", indexKey, v) case bool: fmt.Printf("%s: %tn", indexKey, v) case nil: fmt.Printf("%s: nulln", indexKey) default: fmt.Printf("%s: %v (type %T)n", indexKey, v, v) } } } const input = ` { "outterJSON": { "innerJSON1": { "value1": 10, "value2": 22, "InnerInnerArray": ["test1", "test2"], "InnerInnerJSONArray": [{"fld1": "val1"}, {"fld2": "val2"}] }, "InnerJSON2": "NoneValue" } }`
✅ 关键优势说明:
- 完全动态:不依赖任何预定义 struct,适配任意 JSON Schema;
- 类型安全:通过 switch v := val.(type) 精确识别 map、slice、基础类型,避免 panic;
- 可扩展性强:轻松添加新类型处理逻辑(如 int, int64 —— 注意 JSON 解析后整数也转为 float64);
- 调试友好:输出带路径前缀(如 outterJSON.innerJSON1.fld1),便于定位字段;
- 生产就绪:已处理 nil、布尔、浮点数等边界情况,符合 JSON 规范。
⚠️ 注意事项:
- Go 的 json.Unmarshal 将所有数字统一解析为 float64,如需严格整型,应在业务逻辑中显式转换(例如 int(v.(float64))),并注意精度风险;
- 若 JSON 极深(>1000 层),需警惕递归栈溢出,此时可改用显式栈([]interface{} 模拟)实现迭代版本;
- 对性能极致敏感场景(如高频日志解析),可考虑 github.com/tidwall/gjson(零内存分配、极速查询),但会牺牲遍历灵活性。
掌握此模式,你便拥有了处理任意 JSON 结构的“瑞士军刀”——无论是提取所有字符串字段、查找特定 key、统计嵌套层级,还是构建通用 JSON 转换器,都可在此基础上快速扩展。