golang中Unmarshal的问题

json的规范中,对于数字类型是不区分整形和浮点型的,所以go采用float64来映射,但有个隐患,如果超出float64的安全整数范围,那么就会精度丢失。
当使用 interface{} 接收整数,再次 Marshal 需要注意精度丢失的问题。

float64最大的安全整数是52位尾数全为1且指数部分为最小 0x001F FFFF FFFF FFFF
float64可以存储的最大整数是52位尾数全位1且指数部分为最大 0x07FEF FFFF FFFF FFFF
十进制有效数字在16位(max = 9007199254740991),超过就很可能精度丢失

解决办法:

明确使用int接收,不使用interface{}
使用decoder的方式,设置useNumber=true,解析大数

decoder := json.NewDecoder(bytes.NewReader(request))
decoder.UseNumber() // set useNumber = true
err := decoder.Decode(&req)

设置了UseNumber后,会将数字当作json.Number类型处理,底层是string。

// A Number represents a JSON number literal.
type Number string
// String returns the literal text of the number.
func (n Number) String() string {
    
     return string(n) }
// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
    
    
   return strconv.ParseFloat(string(n), 64)
}
// json.newTypeEcnode -- 记录对应类型的序列化方法

使用tag来实现额外的需求(在指定名称后添加 , 与 参数):

omitempty:json:“name,omitempty” 可以在数值为0或空的时候忽略

string/number/boolean:json:“name,string” 可以制定序列化的时候类型转为string,除此以外还可以选择 number、boolean

综上可得,我们使用接受者要注意以下几点:

接受者需要是指针类型

如果明确了类型,使用struct & 设置json标签为“-”,有助于过滤无需字段,同时相比map可以确定字段类型,减少判断类型的步骤,效果更好

默认使用的decodeState的useNumber为false,且接受者用的类型是interface{},有精度丢失的情况(当超出float64的安全整数范围时)

decoder 与 Unmarshal选择:

json.Decoder适合io.Reader流,或者需要从数据流中解码多个值
json.Unmarshal适合内存中已有 JSON 数据
通过编写MarshalJSON()([]byte, error) 和 UnmarshalJSON(b []byte) error 来实现自定义JSON序列化和反序列化

不要频繁的序列化和反序列化,尽量一次解决

大量的递归和反射影响性能,可以优化(类似protobuf生成代码解析)

猜你喜欢

转载自blog.csdn.net/qq_30505673/article/details/130961401