Golang自定义JSON反序列化:如何保留整数精度存入map[string]any
解决JSON反序列化时整数转int64避免精度丢失的思路
当处理未知结构的JSON时,要把数值尽可能转成int64而非默认的float64,核心思路是绕过默认的float64自动转换,手动控制数值类型解析,具体可通过标准库json.Decoder配合递归处理实现:
核心方案:json.Decoder.UseNumber() + 递归类型转换
默认json.Unmarshal会把所有数值解析为float64,而json.Decoder的UseNumber()方法会将数值保留为json.Number(本质是字符串),这样我们可以手动判断数值类型,再转换为对应的int64/float64。
实现代码
package main import ( "bytes" "encoding/json" "fmt" "log" "math/big" ) func decodeJSON(data []byte) (any, error) { dec := json.NewDecoder(bytes.NewReader(data)) // 启用UseNumber,将数值解析为json.Number而非float64 dec.UseNumber() var val any if err := dec.Decode(&val); err != nil { return nil, err } return convertNumbers(val) } // 递归遍历JSON结构,将json.Number转换为int64/float64(或big.Int处理超大整数) func convertNumbers(val any) (any, error) { switch v := val.(type) { case json.Number: // 优先尝试转换为int64,成功则返回 if intVal, err := v.Int64(); err == nil { return intVal, nil } // 若int64转换失败,尝试用big.Int存储超大整数(可选) var bigInt big.Int if _, ok := bigInt.SetString(string(v), 10); ok { return &bigInt, nil } // 最后转换为float64 floatVal, err := v.Float64() if err != nil { return nil, err } return floatVal, nil case map[string]any: // 递归处理嵌套对象 for key, value := range v { converted, err := convertNumbers(value) if err != nil { return nil, err } v[key] = converted } return v, nil case []any: // 递归处理数组 for i, item := range v { converted, err := convertNumbers(item) if err != nil { return nil, err } v[i] = converted } return v, nil default: // 其他类型(string、bool、null)直接返回 return v, nil } } func main() { jsonData := []byte(`{ "id": 1234567890123456789, "price": 9.99, "count": 42, "hugeInt": 9223372036854775808, "nested": { "bigInt": 9876543210987654321 }, "array": [1, 2.5, 3] }`) result, err := decodeJSON(jsonData) if err != nil { log.Fatal(err) } m := result.(map[string]any) fmt.Printf("id type: %T, value: %v\n", m["id"], m["id"]) // int64, 1234567890123456789 fmt.Printf("price type: %T, value: %v\n", m["price"], m["price"]) // float64, 9.99 fmt.Printf("count type: %T, value: %v\n", m["count"], m["count"]) // int64, 42 fmt.Printf("hugeInt type: %T, value: %v\n", m["hugeInt"], m["hugeInt"]) // *big.Int, 9223372036854775808 nested := m["nested"].(map[string]any) fmt.Printf("bigInt type: %T, value: %v\n", nested["bigInt"], nested["bigInt"]) // int64, 9876543210987654321 array := m["array"].([]any) fmt.Printf("array[0] type: %T, value: %v\n", array[0], array[0]) // int64, 1 fmt.Printf("array[1] type: %T, value: %v\n", array[1], array[1]) // float64, 2.5 }
方案说明
- UseNumber()的作用:阻止解码器自动将数值转为float64,保留原始字符串形式的数值,从根源避免精度丢失。
- 递归处理逻辑:遍历JSON所有嵌套层级(对象、数组),对每个
json.Number做分层判断:- 能转成int64的整数直接存为int64;
- 超出int64范围的超大整数用
big.Int存储(可选,彻底避免精度丢失); - 浮点数或无法转整数的数值转为float64。
- 兼容性:基于标准库实现,无需第三方依赖,支持所有JSON基本类型和嵌套结构。
注意点
- 若业务无需处理超大整数,可以去掉
big.Int转换逻辑,简化代码; - 使用时需要对map中的值做类型断言(比如判断是int64还是float64),因为map的value是
any类型。
内容的提问来源于stack exchange,提问作者Sigma
相关产品推荐
相关产品推荐

