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

简介: 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精度丢失问题并解决

相关文章
|
2月前
|
JSON JavaScript 前端开发
JavaScript实现字符串转json对象的方法
JavaScript实现字符串转json对象的方法
|
2天前
|
JSON 前端开发 JavaScript
json字符串如何转为list对象?
json字符串如何转为list对象?
14 7
|
2天前
|
JSON JavaScript 前端开发
js如何格式化一个JSON对象?
js如何格式化一个JSON对象?
10 3
|
8天前
|
XML JSON JavaScript
JSON对象的stringify()和parse()方法使用
本文阐述了JSON对象的`stringify()`和`parse()`方法的用法,包括如何将JavaScript对象转换为JSON字符串,以及如何将JSON字符串解析回JavaScript对象,并讨论了转换过程中需要注意的事项。
JSON对象的stringify()和parse()方法使用
|
10天前
|
JSON 前端开发 中间件
React读取properties配置文件转化为json对象并使用在url地址中
本文介绍了如何在React项目中读取properties配置文件,将其内容转化为JSON对象,并在请求URL地址时使用这些配置。文章详细说明了异步读取文件、处理字符串转换为JSON对象的过程,并提供了一个封装函数,用于在发起请求前动态生成配置化的URL地址。
26 1
|
9天前
|
JSON 数据库 数据格式
数据库表如果有json字段,该怎么更新
数据库表如果有json字段,该怎么更新
|
2月前
|
JSON 人工智能 编译器
Go json 能否解码到一个 interface 类型的值
Go json 能否解码到一个 interface 类型的值
26 1
|
3月前
|
存储 JSON Java
Java对象转换为JSON字符串
在Java开发中,常需将数据对象转换为JSON存储,如使用Fastjson库。要将Java对象转为JSON,可调用`JSON.toJSONString(obj)`;反向转换则用`JSON.parseObject(str, Class)`。
|
10天前
|
XML 存储 JSON
Twaver-HTML5基础学习(19)数据容器(2)_数据序列化_XML、Json
本文介绍了Twaver HTML5中的数据序列化,包括XML和JSON格式的序列化与反序列化方法。文章通过示例代码展示了如何将DataBox中的数据序列化为XML和JSON字符串,以及如何从这些字符串中反序列化数据,重建DataBox中的对象。此外,还提到了用户自定义属性的序列化注册方法。
27 1
|
7天前
|
存储 JSON Go
在Gin框架中优雅地处理HTTP请求体中的JSON数据
在Gin框架中优雅地处理HTTP请求体中的JSON数据