GoJSON中序列化大整数精度丢失的问题分析

作者:

文章目录
  • 假设我们有一个动态 Extra 字段,用于存储一些扩展信息,其中 oldTask 是一个 int64 大整数: package main import ( “encoding/json” “fmt” ) func main() { // 模拟 Extra 字段 extra := make(map[string]interface{}) extra[“oldTask”] = int64(7587721483007868979) fmt.Println(“【1】原始写入 int64:”) fmt.Printf(“type=%T, value=%vnn”, extra[“oldTask”], extra[“oldTask”]) } 输出: 【1】原始写入 int64:type=int64, value=7587721483007868979 在 Go 内存中,extra["oldTask"] 正确保存了 int64 值。
  • 接下来我们将这个 map 序列化为 JSON 字符串(就像你在 BuildRiskPointsStrategy 中做的): bytes, _ := json.Marshal(extra) fmt.Println(“【2】json.Marshal 后:”) fmt.Println(string(bytes), “n”) 输出: 【2】json.Marshal 后:{"oldTask":7587721483007868979} Go 的 json.Marshal 对 int64 的处理是安全的,大整数不会丢失精度。
  • 然而问题出现了,当我们反序列化 JSON 到 map[string]interface{} 时: var extra2 map[string]interface{} _ = json.Unmarshal(bytes, &extra2) fmt.Println(“【3】json.Unmarshal 后:”) fmt.Printf(“type=%T, value=%vnn”, extra2[“oldTask”], extra2[“oldTask”]) 输出: 【3】json.Unmarshal 后:type=float64, value=7587721483007869000 注意: 反序列化后,extra2["oldTask"] 变成了 float64 原始的 int64 值 7587721483007868979 精度丢失 这是 Go 标准库 encoding/json 的默认行为:map[string]interface{} 中的数字全部解析成 float64。
  • 如果我们强行把它转回 int64: lost := int64(extra2[“oldTask”].(float64)) fmt.Println(“【4】float64 → int64 后:”) fmt.Println(“value =”, lost) 输出: 【4】float64 → int64 后:value = 7587721483007869000 可以看到,数字已经不再精确,丢失了最后几位。
  • fmt.Println(“【5】对比:”) fmt.Println(“是否相等:”, lost == int64(7587721483007868979)) fmt.Println(“差值:”, lost-int64(7587721483007868979)) 输出: 【5】对比:是否相等: false差值: 79 原始值与反序列化后值不一致 这就是“精度丢失”的根本原因
  • 原因总结: 1.Go 的 encoding/json 对 map[string]interface{} 反序列化时: 所有数字默认是 float64 float64 最大安全整数范围是 -2^53 ~ 2^53 2.当 int64 值超出 2^53 时,float64 表示不精确 → 精度丢失 Go 官方文档说明:JSON numbers without decimal points are decoded as float64 when the target is interface{}。
  • Go map[string]interface{} + JSON 是大整数丢失精度的高发场景 精度丢失的原因:JSON 默认解析数字为 float64 解决方案: 使用字符串存储大整数 使用 json.Number 或者尽量使用 struct 明确类型 到此这篇关于Go JSON中序列化大整数精度丢失的问题分析的文章就介绍到这了,更多相关Go JSON序列化内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Go语言中json序列化优化技巧 Golang中自定义json序列化时间格式的示例代码 golang JSON序列化和反序列化示例详解 解决Go Json Unmarshal反序列化丢失数字精度问题 Go语言对JSON数据进行序列化和反序列化 golang如何自定义json序列化应用详解 golang中json反序列化可能遇到的问题
  • 目录
    • 场景示例
    • JSON 序列化
    • JSON 反序列化到 map[string]interface{}
    • 再转回 int64
    • 对比差值
    • 问题分析
    • 解决方案
      • 方案 1:使用字符串存储大整数
      • 方案 2:使用json.Number解析
      • 方案 3:使用 struct 明确类型
    • 结论

      在 Go 项目中,我们经常使用 map[string]interface{} 来表示动态字段,或者用 JSON 序列化对象存入数据库、发送到 MQ。然而,当存储或传输 大整数(int64) 时,往往会出现精度丢失的问题。本文通过一个示例来详细分析原因,并给出解决方案。

      假设我们有一个动态 Extra 字段,用于存储一些扩展信息,其中 oldTask 是一个 int64 大整数:

      package main
      
      import (
      	"encoding/json"
      	"fmt"
      )
      
      func main() {
      	// 模拟 Extra 字段
      	extra := make(map[string]interface{})
      	extra["oldTask"] = int64(7587721483007868979)
      
      	fmt.Println("【1】原始写入 int64:")
      	fmt.Printf("type=%T, value=%vnn", extra["oldTask"], extra["oldTask"])
      }
      

      输出:

      【1】原始写入 int64:
      type=int64, value=7587721483007868979

      在 Go 内存中,extra["oldTask"] 正确保存了 int64 值。

      接下来我们将这个 map 序列化为 JSON 字符串(就像你在 BuildRiskPointsStrategy 中做的):

      bytes, _ := json.Marshal(extra)
      fmt.Println("【2】json.Marshal 后:")
      fmt.Println(string(bytes), "n")
      

      输出:

      【2】json.Marshal 后:
      {"oldTask":7587721483007868979}

      Go 的 json.Marshal 对 int64 的处理是安全的,大整数不会丢失精度。

      然而问题出现了,当我们反序列化 JSON 到 map[string]interface{} 时:

      var extra2 map[string]interface{}
      _ = json.Unmarshal(bytes, &extra2)
      
      fmt.Println("【3】json.Unmarshal 后:")
      fmt.Printf("type=%T, value=%vnn", extra2["oldTask"], extra2["oldTask"])
      

      输出:

      【3】json.Unmarshal 后:
      type=float64, value=7587721483007869000

      注意:

      • 反序列化后,extra2["oldTask"] 变成了 float64
      • 原始的 int64 值 7587721483007868979 精度丢失
      • 这是 Go 标准库 encoding/json 的默认行为:map[string]interface{} 中的数字全部解析成 float64

      如果我们强行把它转回 int64:

      lost := int64(extra2["oldTask"].(float64))
      fmt.Println("【4】float64 → int64 后:")
      fmt.Println("value =", lost)
      

      输出:

      【4】float64 → int64 后:
      value = 7587721483007869000

      可以看到,数字已经不再精确,丢失了最后几位。

      fmt.Println("【5】对比:")
      fmt.Println("是否相等:", lost == int64(7587721483007868979))
      fmt.Println("差值:", lost-int64(7587721483007868979))
      

      输出:

      【5】对比:
      是否相等: false
      差值: 79

      • 原始值与反序列化后值不一致
      • 这就是“精度丢失”的根本原因

      原因总结

      1.Go 的 encoding/jsonmap[string]interface{} 反序列化时:

      • 所有数字默认是 float64
      • float64 最大安全整数范围是 -2^53 ~ 2^53

      2.当 int64 值超出 2^53 时,float64 表示不精确 → 精度丢失

      Go 官方文档说明:JSON numbers without decimal points are decoded as float64 when the target is interface{}

      extra["oldTask"] = fmt.Sprintf("%d", oldTaskInt64)
      
      • JSON 序列化后:"oldTask":"7587721483007868979"
      • 写入数据库 / MQ / ES 时不会丢精度
      • 读取时再用 strconv.ParseInt 转回 int64

      var extra2 map[string]interface{}
      decoder := json.NewDecoder(bytes.NewReader(jsonBytes))
      decoder.UseNumber() // 使用 json.Number 代替 float64
      decoder.Decode(&extra2)
      
      • extra2["oldTask"] 类型是 json.Number
      • 可以安全转成 int64string
      num, _ := extra2["oldTask"].(json.Number).Int64()
      

      type RiskExtra struct {
      	OldTask int64 `json:"oldTask"`
      }
      
      • 避免 map[string]interface{} → float64
      • JSON 反序列化会直接解析为 int64

      Go map[string]interface{} + JSON 是大整数丢失精度的高发场景

      精度丢失的原因:JSON 默认解析数字为 float64

      解决方案:

      • 使用字符串存储大整数
      • 使用 json.Number
      • 或者尽量使用 struct 明确类型

      到此这篇关于Go JSON中序列化大整数精度丢失的问题分析的文章就介绍到这了,更多相关Go JSON序列化内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

      您可能感兴趣的文章:

      • Go语言中json序列化优化技巧
      • Golang中自定义json序列化时间格式的示例代码
      • golang JSON序列化和反序列化示例详解
      • 解决Go Json Unmarshal反序列化丢失数字精度问题
      • Go语言对JSON数据进行序列化和反序列化
      • golang如何自定义json序列化应用详解
      • golang中json反序列化可能遇到的问题

      站内搜索