详解Go语言中json转换int64精度丢失的问题解决

 更新时间:2026年04月20日 09:30:51   作者:leijmdas  
在Go语言中,JSON转换为`int64`类型时,如果JSON中的数字过大或过小,可能会导致精度丢失,本文为大家整理了一些可能导致精度丢失的情况以及具体解决方法,希望对大家有所帮助

在Go语言中,JSON转换为`int64`类型时,如果JSON中的数字过大或过小,可能会导致精度丢失。这是因为JSON标准中并没有定义整数类型,而是使用`double`(双精度浮点数)来表示所有数字,这在转换为`int64`时可能会造成问题。

导致精度丢失的情况

以下是一些可能导致精度丢失的情况:

1. 大整数:如果JSON中的数字超出了`int64`的范围(-2^63 到 2^63-1),那么在转换时可能会丢失精度。

2. 浮点数:如果JSON中的数字是一个浮点数,那么在转换为`int64`时,可能会丢失小数点后的数值。

3. 负数:对于负数,如果它超出了`int64`的表示范围,转换时同样会丢失精度。

解决方法

为了解决这个问题,你可以采取以下几种方法:

1.使用`json.Number`类型:在解析JSON时,可以使用`json.Number`类型来保留原始的数值表示,然后再根据需要转换为`int64`。

import (
    "encoding/json"
    "fmt"
    "math/big"
)
func main() {
    jsonStr := `{"number": "9223372036854775808"}` // 超出int64范围的数字
    var data map[string]json.Number
    err := json.Unmarshal([]byte(jsonStr), &data)
    if err != nil {
        panic(err)
    }
    number := data["number"]
    num := new(big.Int)
    num, ok := number.Int64()
    if !ok {
        num, _ = new(big.Int).SetString(string(number), 10)
        fmt.Printf("转换为big.Int: %s\n", num.String())
    } else {
        fmt.Printf("转换为int64: %d\n", num)
    }
}

2.使用第三方库:使用如`go.mongodb.org/mongo-driver/bson`这样的库,它们可能提供了更灵活的整数处理方式。

3.手动处理:在解析JSON之前,你可以手动检查数字的大小,如果超出了`int64`的范围,可以提前进行处理。

4.明确JSON格式:在生成JSON时,确保数字在`int64`的范围内,或者使用字符串来表示大整数。

请注意,处理大整数或浮点数时,可能需要使用`math/big`包中的`Int`类型来避免精度丢失。

jsoniter "github.com/json-iterator/go"
func Test0008_Jsoniter_Marshal(t *testing.T) {
    order := Order{
       Id:         baseutils.SnowflakeNextVal(),
       OrderId:    "12345678",
       Money:      99.99,
       CreateTime: time.Now(), // 2023-12-05T16:19:33.943989108+08:00
       Extend:     map[string]string{"name": "张三"},
    }
    // 使用1:直接转成字符串
    jsonStr, _ := jsoniter.MarshalToString(order)
    fmt.Println("jsonStr:", jsonStr)
    // 使用2:直接转成字节数组
    jsonByteArr, _ := jsoniter.Marshal(order)
    fmt.Println("jsonByteArr:", jsonByteArr)
    // 使用3:反序列化之字符串转结构体
    str := `{"id":666, "order_id":"12345678", "money": 99.99, "create_time":"2023-12-05T16:19:33.943989108+08:00", "extend":{"name":"张三"}}`
    var order2 Order
    err := jsoniter.UnmarshalFromString(str, &order2)
    if err != nil {
       fmt.Println("err2:", err)
    }
    fmt.Println("order2:", order2)
    // 使用4:反序列化之字节数组转结构体
    var order3 Order
    var jsonNew = jsoniter.ConfigCompatibleWithStandardLibrary
    // 自适应类型
    extra.RegisterFuzzyDecoders()
    err = jsonNew.Unmarshal(jsonByteArr, &order3)
    if err != nil {
       fmt.Println("err3:", err)
    }
    fmt.Println("order3:", order3)
}
​​​​​​​order3: {572839995925069824 12345678 99.99 2024-04-29 17:41:37.8819085 +0800 CST map[name:张三]}

知识扩展

处理 Go 语言 JSON 转换时 int64 精度丢失的问题,本质是 encoding/json 默认将 JSON 数字解析为 float64 导致的。由于 float64 仅能精确表示 ±2^53 范围内的整数,超出这个范围的 64 位整数在转换时会被舍入或截断,从而引发精度丢失。

针对此问题,业界主要有三种不同的解决方案,它们各有优劣,你可以根据自己的具体场景来权衡选择。

方案一:使用 json.Number (最轻量、最精准)

这种方案通过延迟类型转换来避免精度丢失。你可以将结构体中可能包含大整数的字段声明为 json.Number 类型(本质是字符串),它就像一个“容器”,暂存原始的 JSON 数字文本。之后,你可以按需通过调用其 .Int64() 方法来获得精确的 int64 值。

package main
import (
	"encoding/json"
	"fmt"
)
type MyData struct {
	// 将可能超限的整数字段声明为 json.Number
	LargeID json.Number `json:"id"`
}
func main() {
	jsonStr := `{"id": 9223372036854775807}`
	var data MyData
	if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
		panic(err)
	}
	// 安全地将 json.Number 转换为 int64
	id, err := data.LargeID.Int64()
	if err != nil {
		panic(err) // 处理转换错误
	}
	fmt.Printf("转换后的 int64 值: %d\n", id)
}

关键点json.Number 字段需要手动调用 .Int64() 方法进行转换,这是获取精确值的必要步骤,不可省略。

方案二:使用 json:",string" 标签 (最优雅、最常用)

此方案利用结构体标签,让 encoding/json 在编解码时自动将该字段在 JSON 字符串和 Go 数值之间进行转换。它在你无法改变前端(如 JavaScript)只接收字符串的情况下尤其有用。

package main

import (
	"encoding/json"
	"fmt"
)

type MyData struct {
	// 使用 json:",string" 标签,让 ID 在 JSON 中以字符串形式传递
	LargeID int64 `json:"id,string"`
}

func main() {
	jsonStr := `{"id": "9223372036854775807"}`
	var data MyData
	if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
		panic(err)
	}
	fmt.Printf("Unmarshal 得到的 int64 值: %d\n", data.LargeID)
}

方案三:自定义 UnmarshalJSON 方法 (终极可控)

此方案通过实现 json.Unmarshaler 接口来完全控制一个自定义类型的解析过程,获得最高的灵活性。你可以将解析逻辑封装成一个自定义类型,并在需要的地方复用。

package main

import (
	"encoding/json"
	"fmt"
	"strconv"
)

// 定义一个新的类型,并为其实现 UnmarshalJSON 接口
type PreciseInt int64

func (pi *PreciseInt) UnmarshalJSON(b []byte) error {
	// 1. 先解析到 json.Number
	var n json.Number
	if err := json.Unmarshal(b, &n); err != nil {
		return err
	}
	// 2. 再从 json.Number 安全地转换为 int64
	i, err := n.Int64()
	if err != nil {
		return err
	}
	*pi = PreciseInt(i)
	return nil
}

type MyData struct {
	LargeID PreciseInt `json:"id"`
}

func main() {
	jsonStr := `{"id": 9223372036854775807}`
	var data MyData
	if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
		panic(err)
	}
	fmt.Printf("转换后的 int64 值: %d\n", data.LargeID)
}

方案对比与选择建议

为了帮助你更直观地选择,我为你整理了以下对比表格:

对比维度方案一:json.Number方案二:json:",string" 标签方案三:自定义 UnmarshalJSON
核心机制使用 json.Number 类型暂存原始 JSON 数字字符串利用标签 ,string 实现 JSON 字符串与 Go 数值的自动转换为自定义类型实现 json.Unmarshaler 接口,完全掌控解析过程
代码侵入性:只需修改结构体字段类型:只需在结构体标签中添加 ,string中等:需要定义新类型并实现接口
对前端的影响:前端接收到的仍是 Number:前端需处理 string 类型可控:可根据实现逻辑决定 JSON 中的格式
性能开销中等:涉及额外类型转换,有约 2-3 倍开销:性能损耗极小中等:类似 json.Number,取决于实现
灵活性与扩展性:功能相对单一:功能单一极高:可根据需求定制任意逻辑
推荐场景处理动态结构第三方 API 的 JSON 数据时JavaScript 前端交互时的首选方案需要对特定字段进行复杂校验、转换或类型限制

总的来说:

  • 如果你需要和前端(尤其是 JavaScript)交互,方案二 (json:",string" 标签) 是最直接、优雅的选择。
  • 如果你正在处理一个结构不固定的 JSON 数据,方案一 (json.Number) 能提供最大的灵活性。
  • 如果以上方案都无法满足你的特定需求,方案三 (自定义 UnmarshalJSON) 为你提供了终极的掌控力。

到此这篇关于详解Go语言中json转换int64精度丢失的问题解决的文章就介绍到这了,更多相关Go语言json转int64精度丢失解决内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 如何用golang运行第一个项目

    如何用golang运行第一个项目

    这篇文章主要介绍了如何用golang运行第一个项目,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • 深入剖析Go语言中数组和切片的区别

    深入剖析Go语言中数组和切片的区别

    本文将深入探讨 Go 语言数组和切片的区别,包括它们的定义、内存布局、长度和容量、初始化和操作等方面。从而更好地在实际开发中选择和使用合适的数据结构,提高代码的效率和可维护性,需要的可以参考一下
    2023-05-05
  • 利用go-zero在Go中快速实现JWT认证的步骤详解

    利用go-zero在Go中快速实现JWT认证的步骤详解

    这篇文章主要介绍了如何利用go-zero在Go中快速实现JWT认证,本文分步骤通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2020-10-10
  • 解决GOPATH在GOLAND中的坑

    解决GOPATH在GOLAND中的坑

    这篇文章主要介绍了解决GOPATH在GOLAND中的坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • golang API开发过程的中的自动重启方式(基于gin框架)

    golang API开发过程的中的自动重启方式(基于gin框架)

    这篇文章主要介绍了golang API开发过程的中的自动重启方式(基于gin框架),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Go语言使用defer+recover解决panic导致程序崩溃的问题

    Go语言使用defer+recover解决panic导致程序崩溃的问题

    如果协程出现了panic,就会造成程序的崩溃,这时可以在goroutine中使用recover来捕获panic,进行处理,本文就详细的介绍一下,感兴趣的可以了解一下
    2021-09-09
  • go并发编程sync.Cond使用场景及实现原理

    go并发编程sync.Cond使用场景及实现原理

    这篇文章主要为大家介绍了go并发编程sync.Cond使用场景及实现原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 使用Go语言简单模拟Python的生成器

    使用Go语言简单模拟Python的生成器

    这篇文章主要介绍了使用Go语言简单模拟Python的生成器,Python的generator是非常酷的功能,用Go实现的代码也较为简洁,需要的朋友可以参考下
    2015-08-08
  • Go 语言中程序编译过程详解

    Go 语言中程序编译过程详解

    本文旨在深入探讨Go语言的编译机制和最新的模块管理系统——Go Modules,通过详细的示例和步骤,我们将演示从简单的 “Hello World” 程序到使用第三方库的更复杂项目的开发过程,感兴趣的朋友跟随小编一起看看吧
    2024-05-05
  • Go语言使用Redis和Etcd实现高性能分布式锁

    Go语言使用Redis和Etcd实现高性能分布式锁

    这篇文章主要为大家介绍了Go语言使用Redis实现高性能分布式锁示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12

最新评论