应优先用json.Unmarshal解析为map[string]interface{}或[]interface{},再通过类型断言和递归函数搜索任意层级键值,而非直接使用反射;数字默认为float64需显式转换,访问须逐层断言,不可链式调用。

用 map[string]interface{} 解析后再递归搜索,别一开始就上反射
不确定深度的 JSON,第一反应不该是写反射代码,而是先用 json.Unmarshal 解到 map[string]interface{} 或 []interface{}。Go 的 encoding/json 原生支持这种泛化解析,能保住所有嵌套层级,且不会 panic——而手写反射构造结构体稍有不慎(比如字段名含连字符、key 是数字开头)就会在 reflect.StructOf 阶段直接崩溃。
常见错误现象:panic: reflect.StructOf: field "2024_config" is not a valid identifier,或解析后字段全为零值但无报错。
- 必须先
json.Unmarshal(data, &m),传地址,否则m仍是 nil - JSON 中的数字一律转成
float64,后续要用int(v.(float64))显式转换 - 嵌套对象是
map[string]interface{},数组是[]interface{},访问时必须逐层断言,不能跳步(如不能m["data"]["translations"][0]["translatedText"]直接链式调用)
写递归函数遍历 interface{},而不是用反射查结构体字段
要找某个 key(比如 "translatedText")在任意深度出现的值,正确做法是写一个类型安全的递归搜索函数,对 interface{} 做 switch t := v.(type) 分支处理。这不是反射的典型用法,但比用 reflect.ValueOf 去遍历结构体字段更轻量、更可控。
容易踩的坑:go-simplejson 等第三方库的 Get("x").Get("y") 只查当前层级;而你自己写的递归函数得明确区分 map[string]interface{} 和 []interface{} 的遍历逻辑,否则会漏掉数组里的对象。
立即学习“go语言免费学习笔记(深入)”;
- 对
map[string]interface{}:先检查当前 map 是否含目标 key;若无,则对每个 value 递归调用 - 对
[]interface{}:遍历每个元素,对每个 element 递归调用 - 对其他类型(string/float64/bool/nil):直接返回,不递归
- 返回第一个匹配值即可,或收集全部匹配项——取决于业务是否允许多个同名 key
只有当你需要把 map 动态转成结构体时,才真得动用 reflect.StructOf
比如你拿到一段 webhook 的 custom_fields,键名全是运行时才知道的字符串("user-id"、"score_2024"),又必须把它塞进一个带类型、带 tag、能被 validator 或 ORM 消费的结构体里,这时才轮到 reflect.StructOf 出场。
它不是用来“解析 JSON”的,而是用来“构造一个能被 json.Unmarshal 接收的结构体类型”。这个过程本身不碰 JSON 数据,只基于 key 名生成字段定义。
- 字段名必须是合法 Go 标识符:把
"user-id"转成"UserID",再设json:"user-id"tag - 嵌套结构不能递归调用
StructOf:得先对子 JSON 单独Unmarshal成map[string]interface{},再基于它的 key 列表推导子字段 - 切片字段类型不能硬写
[]string:得看第一个元素类型,用reflect.SliceOf(reflect.TypeOf(""))动态构造 - 构造完类型后,必须用
reflect.New(dynamicType).Interface()得到指针,再传给json.Unmarshal
json.Unmarshal 本身已用反射,你传错地址就白忙活
很多人写了半天反射逻辑,最后发现字段还是零值,原因往往极简单:json.Unmarshal(data, v) 传的是值,不是地址。无论你用 map、struct 还是 reflect.New(t).Interface() 构造的类型,都必须确保传进去的是指针。
这个错误不报 panic,只静默失败——数据解析成功,但没写进你期望的变量里。调试时打印 reflect.ValueOf(v).Kind(),如果不是 Ptr,基本可以停手了。
-
var m map[string]interface{}→ 必须json.Unmarshal(data, &m) -
v := reflect.New(t).Interface()→v已是指针,可直接传 -
v := reflect.Zero(t).Interface()→ 得改成&v再传 - 结构体字段嵌套未导出(首字母小写)→ 深层字段会被静默忽略,不报错也不赋值
map[string]interface{} 能解决 80% 的动态 JSON 场景;递归搜索适合“捞值”;而 reflect.StructOf 只在必须产出强类型结构体时才值得投入——一旦选错层级,后续所有类型断言和字段访问都会变得脆弱。


















