直接用json.unmarshal解析到map[string]interface{}可行,但不加类型断言就取值会panic,因json数组→[]interface{}、对象→map[string]interface{},data"items"["name"]需先断言data["items"]为[]interface{},再断言元素为map[string]interface{},最后断言"name"为string或float64。
![go 中 json.unmarshal 函数的类型断言:解析未知 json 结构体到 map[string]interface{}](https://img.php.cn/upload/article/001/589/237/178520676123686.jpeg?x-oss-process=image/resize,p_40)
直接用 json.Unmarshal 解析到 map[string]interface{} 是可行的,但不加类型断言就取值,99% 会 panic。
为什么 data["items"][0]["name"] 会 panic
因为 json.Unmarshal 把 JSON 数组解析成 []interface{},不是切片;把对象解析成 map[string]interface{},不是结构体。你写的 data["items"][0]["name"] 实际上在对一个 interface{} 做两次下标操作——Go 不允许。
-
data["items"]返回的是interface{},必须先断言为[]interface{} -
items[0]返回的仍是interface{},得再断言为map[string]interface{} - 最后才能用
item["name"],且仍需断言类型(比如string或float64)
数字字段为什么总是 float64,怎么拿到 int64
JSON 规范里没有整型/浮点型区分,Go 默认全当 float64 解析。大 ID(如 1234567890123456789)转 float64 会丢精度。
- 启用
json.UseNumber():用json.NewDecoder(r).UseNumber()替代json.Unmarshal - 字段值变成
json.Number(本质是字符串),可安全调用.Int64()或.Float64() - 别直接用
==比较两个json.Number,得先转成具体类型再比
数组里的字符串切片怎么安全取出来
JSON ["a","b"] 解析后是 []interface{},不是 []string。强行 v["tags"].([]string) 会 panic。
- 先断言为
[]interface{},再遍历每个元素,逐个转成string - 示例:
if raw, ok := v["tags"].([]interface{}); ok { tags := make([]string, len(raw)); for i, s := range raw { tags[i] = s.(string) } } - 如果数组里混了非字符串(比如
null或数字),s.(string)会 panic,得加ok判断
更稳的做法:用 json.RawMessage 延迟解析
当你只对部分字段动态处理,其余字段结构固定时,硬套三层断言既啰嗦又易错。
- 定义结构体,把已知字段写死,未知字段设为
json.RawMessage -
json.Unmarshal一次进结构体,不会触发类型转换,保留原始字节 - 后续只对
RawMessage字段单独json.Unmarshal到map[string]interface{}或具体 struct - 避免了数字精度丢失、数组类型误判、嵌套断言嵌套 panic 等所有常见坑
真正麻烦的从来不是解析本身,而是你忘了 interface{} 不是万能容器——它什么都没告诉你,只等你亲手把它一层层剥开。











