Go语言如何解析非结构化JSON并递归提取所有嵌套键值对
非结构化JSON全量递归解析方案
核心问题根源
你之前直接用json.Unmarshal([]byte(input), &customers)的写法,适配场景是固定schema的结构化JSON解析——也就是提前明确知道JSON所有层级的字段、类型、嵌套关系,才能定义对应的Go结构体做映射。
但你现在面对的是嵌套层级无严格固定、存在多组数组和嵌套对象的非结构化JSON,提前写死的结构体没法覆盖所有深层节点,既会漏解析字段,也实现不了“遇到嵌套对象/数组就持续向下遍历”的需求。
实现思路
对于非结构化JSON,反序列化时不要用自定义结构体接收,直接用Go的通用空接口any(即旧版本的interface{})承接所有解析结果,再通过类型断言逐节点判断类型,递归向下遍历:
- 遇到JSON对象(对应Go的
map[string]any类型):先判断当前节点是否为需要提取的目标节点,再对对象内的每个值继续递归遍历,不遗漏深层嵌套内容 - 遇到JSON数组(对应Go的
[]any类型):逐个数组元素做递归判断 - 遇到字符串、数字、布尔值等基础类型:直接处理输出,停止向下遍历
可落地的实现代码
package main import ( "encoding/json" "fmt" ) // traverse 递归遍历任意深度的JSON节点 func traverse(node interface{}) { switch currentVal := node.(type) { // 处理JSON对象节点 case map[string]interface{}: // 匹配目标节点:带key字段的组件对象 if keyStr, ok := currentVal["key"].(string); ok { var targetValue interface{} // 优先取节点自身的value字段作为输出值,没有则取values数组 if v, exists := currentVal["value"]; exists { targetValue = v } else if vList, exists := currentVal["values"]; exists { targetValue = vList } // 基础类型直接输出原始值,复杂类型(对象/数组)做格式化序列化 if targetValue != nil { switch v := targetValue.(type) { case string: fmt.Printf("Key: %s Value: %s\n", keyStr, v) default: valByte, _ := json.MarshalIndent(targetValue, "", " ") fmt.Printf("Key: %s Value: %s\n", keyStr, string(valByte)) } } } // 继续递归遍历对象内所有值,不遗漏深层嵌套节点 for _, child := range currentVal { traverse(child) } // 处理JSON数组节点 case []interface{}: for _, item := range currentVal { traverse(item) } // 基础类型节点无需继续遍历 default: return } } func main() { // 你的输入JSON字符串(已修正原样例里的尾随逗号语法错误) inputJSON := `{ "components": [ { "key": "d1", "components": [ { "key": "custname", "value": "Abraham", "input": true, "tableView": true }, { "key": "type", "type": "radio", "label": "Fisrt", "values": [ { "label": "Sole", "value": "sole", "shortcut": "" }, { "label": "Bata", "value": "Bata", "shortcut": "" } ], "validate": { "required": true }, "tableView": false }, { "key": "registeredField", "value": "reg 111", "input": true }, { "key": "dirc", "value": "abraham" }, { "key": "gst", "value": "textfield", "useLocaleSettings": false }, { "key": "pan", "value": "AAAAA0000", "useLocaleSettings": false } ], "collapsible": false } ] }` // 用通用any类型承接反序列化结果,不绑定自定义结构体 var rawRoot interface{} err := json.Unmarshal([]byte(inputJSON), &rawRoot) if err != nil { fmt.Printf("JSON解析失败: %v\n", err) return } // 从根节点开始递归全量遍历 traverse(rawRoot) }
输出效果
代码运行后会完全匹配你期望的输出格式,依次打印所有带key字段的组件值:
Key: custname Value: Abraham Key: type Value: [ { "label": "Sole", "value": "sole", "shortcut": "" }, { "label": "Bata", "value": "Bata", "shortcut": "" } ] Key: registeredField Value: reg 111 Key: dirc Value: abraham Key: gst Value: textfield Key: pan Value: AAAAA0000
注:原样例JSON存在多处尾随逗号的语法问题(比如
"input": true,行尾多余逗号),实际运行时需先修正这类语法错误,否则会触发JSON解析失败。
内容的提问来源于stack exchange,提问作者RohanDsouza
相关产品推荐
相关产品推荐

