You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
To unmarshal JSON into an interface value, Unmarshal stores one of these in the interface value:
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
encoding/json 是 Go 代码经常使用的包,但是,可能很多人都会忽略下面这段说明:
当 json 解码到 interface 类型的变量值时,会将 JSON numbers(实质是 string 类型,表示整数或浮点数数字字符串)都当作类型 float64 存储。
试想以下代码输出?
完成 Json 解码后,
Timestamp
类型为 float64。这显然是无法让人接受的,就这里来说,时间戳应该是 int64 才对。目前,有两个解决办法:避免使用 interface,而是直接静态类型指定,在大多数情况下,Json 字符串结构都是已知的,静态的。
上面的场景,就可以将时间戳属性定义为
Timestamp int64
。func (*Decoder) UseNumber() 使解码器将数字作为 json.Number 类型,
而不 float64 解码到 interface 变量。
可以看到,json.Number 其实就是字符串类型:
因此,这里其实就是保留原始字符串,延迟解析。在需要的时候,使用提供的函数
Float64()
,Int64()
等转化成对应的类型,其实,这些函数的实现就是使用strconv
包将字符串转化成整型或浮点型。但是,这里引入了一个新的类型 json.Number,会侵入到别的无关的代码中,也就是说,可能会导致,在其它模块,不得不在类型判断时,加入 json.Number case。这种耦合是比较让人难受的。
遗憾的是,目前看来,只有这两种方式了,虽然都不够优雅。
这是个很奇怪的问题,因为技术上来说,将数字字符串分别解析为整型或浮点型并不难实现,Go 编译器就很好的实现了(想想
x:=100
与x:=100.0
的区别);而且,如果 Json 数字的含义是整型,默认却解析成 float64 就会有精度丢失的问题,因为 int64 比 float64 表示的范围更大。
去 Go issues 找了下,也并没有看到合理的解释,难道只是为了实现方便,偷了个懒?真是个奇怪的坑!
相关 issues:
补充
感谢Go 论坛网友 @H12 的指正:
Go 中的 float64 其实等同于
double
类型:范围是
-2^1024 ~ +2^1024
,也即-1.79E+308 ~ +1.79E+308
。精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响:2^52 = 4503599627370496,一共16位,因此,double的精度为15~16位。而 int64 等同于 long, 占8个字节,表示范围:-9223372036854775808 ~ 9223372036854775807
。因此,出现这个坑的原因,是设计上的取舍,为了保证 Json 数字解析安全(不溢出),只能牺牲精度。
The text was updated successfully, but these errors were encountered: