• json.Unmarshal() 反序列化字节流到 interface{} 对象,字段 int/int64 类型出现精度丢失


    问题描述

    今天遇到一个 json.Unmarshal() 反序列化字节流到 interface{} 对象,int/int64 类型出现精度丢失的问题,记录一下。下面是网上其他同学的类似的代码,跟我的场景很像,所以直接拿过来作为案发现场代码用了。

    	jsonStr := `{"id":3861708980690657283}`
    	result := make(map[string]interface{})
    	err := json.Unmarshal([]byte(jsonStr), &result)
    	if err != nil {
    		fmt.Println(err)
    	}
    	id := result["id"]
    	fmt.Printf("type=%T, val=%v\n",id, id)
    

    输出

    type=float64, val=3.861708980690657e+17
    

    反序列化得到的 id 字段变成了 float64 类型,值输出形式也变成了科学计数法形式,这当然难不倒我,略一百度,就查到了 json.Unmarshal() 反序列化字节流到 interface{} 对象时,如果原来是 int 类型,会被反序列化成 float64 类型,网上的解决方案是对 int 字段进行类型强转

    	id := int64(result["id"].(float64)) // 先强转成 float64类型,再强转成int64 类型
    	fmt.Printf("type=%T, val=%v\n",id, id)
    

    输出

    type=int, val=3861708980690657280
    

    查看输出结果,可以发现,类型变成了 int 类型,val 表现形式也变成了整数形式,但是出现了精度丢失,原来值是 3861708980690657283,变成了 3861708980690657280,最后一位的精度丢失了。

    再次开始网上冲浪,通过参考中提到的 2 篇博客发现了有 2 种解决方案:

    解决方案

    1、使用 decode+UseNumber()

    不使用 json.Unmarshal() 来反序列对象,而是采用 decode+UseNumber() 来实现反序列化。

    	jsonStr := `{"id":3861708980690657283}`
    	result := make(map[string]interface{})
    	decoder := json.NewDecoder(bytes.NewBufferString(jsonStr))
    	decoder.UseNumber() // 指定使用 Number 类型
    	err := decoder.Decode(&result)
    	if err != nil {
    		fmt.Println(err)
    	}
    	id := result["id"]
    	fmt.Printf("type=%T, val=%v\n",id, id)
    

    输出

    type=json.Number, val=3861708980690657283
    

    输出结果精度没有丢失,类型是 json.Number。如果想把 id 转换成 int4 类型,需要先转换成字符串,再强转成 int64 类型

    	aaa, err := strconv.ParseInt(fmt.Sprintf("%v", id), 10, 64)  // 如果想把 id 转换成 int4 类型,需要先转换成字符串,再强转成 int64 类型
    	fmt.Printf("type=%T, val=%v\n",aaa, aaa)  // 输出 type=int64, val=3861708980690657283
    
    

    2、反序列化的对象改成不是 interface{}, 而是自定义结构体

    可以发现上面先后遇到的这两个问题,无论是科学计数法输出,还是精度丢失,都是发生在 Unmarshal反序列化字节流到 interface{} 对象 上,如果如果不是 interface{} 对象,是不会有这个问题的,所以另一种解决方案是,自定义结构体,明确指定每个字段的类型,这样一切都会如你所愿,不会遇到上面任何一个问题。

    	type Student struct {
    		ID int64 `json:"id"`
    	}
    
    	jsonStr := `{"id":3861708980690657283}`
    	var result Student
    	err := json.Unmarshal([]byte(jsonStr), &result)
    	if err != nil {
    		fmt.Println(err)
    	}
    	id := result.ID
    	fmt.Printf("type=%T, val=%v\n",id, id)
    

    输出

    type=int64, val=3861708980690657283
    

    by the way

    网友建议前后端在使用 int64 类型进行交互时,尽量将 int64 转成 string 来传输。

    参考:[系列] Go - json.Unmarshal 遇到的小坑golang int64 json.Unmarshal精度丢失问题并解决

  • 相关阅读:
    [C#] 逆袭——自制日刷千题的AC自动机攻克HDU OJ
    [安卓] 13、安卓蓝牙定位(一)——如何周期性获得蓝牙节点信号强度?
    [stm32] NRF24L01+USART搞定有线和无线通信
    [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏
    [安卓] 11、串口蓝牙·将软硬结合进行到底
    [安卓] 10、悬浮窗与获取其他任务信息
    [安卓] 9、线程、VIEW、消息实现从TCP服务器获取数据动态加载显示
    [安卓] 8、VIEW和SURFACEVIEW游戏框架
    [安卓] 7、页面跳转和Intent简单用法
    Git常用命令记录
  • 原文地址:https://www.cnblogs.com/hi3254014978/p/16702845.html
Copyright © 2020-2023  润新知