JSON数字Unmarshal至interface{}后截断,如何解决数值失真问题?
问题
我有一个包含多字段的JSON,按照Stack Overflow《How effectively to change JSON keys》里的方法循环处理删除不需要的键后,原有JSON的数值出现了变化——部分变成浮点数,原本的1684366653200744506被截断成了1684366653200744400。演示输出如下:
2009/11/10 23:00:00 1684366653200744448.000000
2009/11/10 23:00:00 map[timestamp:1.6843666532007444e+18]
2009/11/10 23:00:00 json Marshal from maps of key string and value interface to batch json for insert to DB
2009/11/10 23:00:00 {"timestamp":1684366653200744400}
想搞清楚:
- 为什么数值会被截断?
- 是不是
interface{}导致的这个问题? - 怎么改才能避免这种情况?
分析与解决方法
原因说明
- 浮点数精度限制:Go的
encoding/json库默认解析JSON时,若用interface{}接收整数,会自动转成float64类型。但float64的有效精度只有53位,你那个19位的整数远超出这个范围,存储时只能做近似处理,自然就被截断了。 - 和
interface{}的关系:不是interface{}本身有问题,而是当你用map[string]interface{}存储解析后的JSON数据时,整数会被默认转成float64,后续序列化输出的就是近似后的数值。
解决办法
1. 用结构体替代map[string]interface{}
定义一个和JSON结构匹配的结构体,把需要的字段类型指定为int64(或uint64,根据数值范围选择),这样解析时会直接保留整数类型,不会转成浮点数。示例代码:
type Data struct { Timestamp int64 `json:"timestamp"` // 其他需要保留的字段 } var data Data err := json.Unmarshal(rawJSON, &data) // 后续处理这个结构体即可,序列化时数值不会丢失精度
2. 使用json.Number类型
解析JSON时,开启UseNumber()选项,让整数以json.Number类型存储(本质是字符串),避免转成浮点数。示例:
var result map[string]interface{} decoder := json.NewDecoder(strings.NewReader(rawJSON)) decoder.UseNumber() err := decoder.Decode(&result) // 需要使用数值时,将json.Number转为int64 timestamp, err := result["timestamp"].(json.Number).Int64()
3. 自定义JSON解析逻辑
如果需要动态处理字段(比如删除不需要的键),可以用json.RawMessage保留原始JSON片段,处理完成后再解析成对应类型,以此避免精度丢失。
内容的提问来源于stack exchange,提问作者echo
相关产品推荐
相关产品推荐

