json.unmarshal默认将所有json数字解析为float64,因json标准无整型/浮点型区分;用structs库或手写反射可保原始类型并支持标签控制。
![如何将结构体快速转换为 map[string]interface{}](https://img.php.cn/upload/article/001/246/273/177736170027204.jpg?x-oss-process=image/resize,p_40)
直接用 json.Marshal + json.Unmarshal 最快,但会把所有数字转成 float64;需要保类型就别用它,改用反射或 structs 库。
为什么 json.Marshal/Unmarshal 会把 int 变成 float64
JSON 标准里没有整型和浮点型之分,统一叫 Number。Go 的 encoding/json 在反序列化时,对所有数字字段默认解析为 float64,哪怕原始结构体字段是 int、int64 或 uint。
- 现象:
m["age"]值是18,但fmt.Printf("%T", m["age"])输出float64 - 后果:后续做类型断言(如
v.(int))会 panic;传给期望int的函数时报错 - 注意:这个行为不可配置,
json.Decoder也一样,没开关能关掉
用 structs 库避免类型丢失且支持标签控制
structs 库基于反射,不经过 JSON 中间层,字段值保持原类型,还能通过 structs 标签精细控制键名、忽略、omitempty 等逻辑。
- 安装:
go get github.com/fatih/structs - 基础用法:
structs.Map(u)或structs.ToMap(u)(两者等价) - 字段重命名:
Name string `structs:"user_name"`→ key 变成"user_name" - 忽略字段:
Token string `structs:"-"`→ 不出现在 map 中 - 空值跳过:
Desc string `structs:"desc,omitempty"`→ 若Desc == "",则整个键不写入
手写反射转换时要注意指针和 tag 名
自己写 ToMap 函数可控性最强,但容易漏掉边界情况。
- 必须处理指针:先判断
v.Kind() == reflect.Ptr,再调v.Elem() - tag 名要传对:比如结构体用
json:"name",调用时就得传"json";若混用mapstructure或gorm标签,得同步改参数 - 嵌套结构体不会自动展开:默认只转一层,
Address struct{...}字段在 map 中仍是interface{}类型,不是嵌套 map —— 想递归得自己加逻辑 - 未导出字段(小写开头)会被跳过:反射无法访问,
v.Field(i).Interface()对私有字段返回零值
最常被忽略的是嵌套结构体的处理方式和私有字段的静默丢弃——这两点在调试时往往表现为“某些字段不见了”或“嵌套内容变成 &{...}”,而不是报错,排查成本高。










