首页 > 后端开发 > Golang > 正文

Go JSON 解析:避免 int64 精度丢失的两种策略

聖光之護
发布: 2025-10-15 09:19:24
原创
1019人浏览过

Go JSON 解析:避免 int64 精度丢失的两种策略

本文探讨在 go 语言中解析 json 时,如何有效避免大整数(如 `int64`)被默认转换为 `float64` 导致精度丢失的问题。我们将介绍两种核心策略:一是利用 `json.decoder` 的 `usenumber()` 方法将数字作为字符串处理,再手动转换;二是定义具有 `uint64` 或 `int64` 字段的 go 结构体进行直接解析,确保数据完整性。

Go JSON 解析中的 int64 精度丢失问题

在 Go 语言中处理 JSON 数据时,encoding/json 包的 json.Unmarshal 函数在默认情况下会将 JSON 中的数值类型(尤其是大整数)解析为 float64。这对于一般的浮点数或较小的整数可能不是问题,但当遇到超出 float64 精确表示范围的 64 位整数(如 int64 或 uint64)时,这种自动转换会导致精度丢失,从而获取到不正确的数据。

例如,当解析一个包含 4418489049307132905 这样大整数的 JSON 字符串时,如果直接将其映射到 map[string]interface{},Go 会将其识别为 float64。尝试将其断言为 int64 将会失败,因为底层类型已不再是整数,并且 float64 自身也可能无法精确表示该大整数。

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    body := []byte(`{"tags":[{"id":4418489049307132905},{"id":4418489049307132906}]}`)

    var dat map[string]interface{}
    if err := json.Unmarshal(body, &dat); err != nil {
        panic(err)
    }

    tags := dat["tags"].([]interface{})
    // 尝试直接断言为 int64 会导致运行时错误,因为实际类型是 float64
    // fmt.Println("tag: ", 0, " id: ", tags[0].(map[string]interface{})["id"].(int64))

    // 实际输出会是 float64,且可能已发生精度丢失
    fmt.Printf("Parsed ID type: %T, value: %.0f\n", tags[0].(map[string]interface{})["id"], tags[0].(map[string]interface{})["id"])
    // 示例输出可能为:Parsed ID type: float64, value: 4418489049307132904
    // 注意:原始值是 4418489049307132905,这里已经发生了精度丢失。
}
登录后复制

为了解决这一问题,Go 提供了两种主要策略来确保 64 位整数在 JSON 解析过程中不丢失精度。

解决方案一:使用 json.Decoder 配合 UseNumber()

encoding/json 包中的 json.Decoder 提供了比 json.Unmarshal 更精细的控制。通过调用 Decoder 的 UseNumber() 方法,我们可以指示解码器将所有 JSON 数字解析为 json.Number 类型,而不是 float64。json.Number 本质上是一个字符串类型,它完整地保留了原始数字的文本表示,从而避免了任何精度丢失。之后,我们可以根据需要将 json.Number 转换为 int64 或 uint64。

实现步骤:

  1. 创建一个 json.Decoder 实例,从 bytes.Buffer 或其他 io.Reader 中读取 JSON 数据。
  2. 调用 d.UseNumber() 启用数字的 json.Number 解析模式。
  3. 使用 d.Decode() 将数据解码到 map[string]interface{} 或其他泛型结构中。
  4. 当访问数字字段时,将其断言为 json.Number 类型。
  5. 使用 strconv 包中的 ParseUint 或 ParseInt 函数将 json.Number 字符串转换为所需的整数类型。

示例代码:

package main

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

func main() {
    body := []byte(`{"tags":[{"id":4418489049307132905},{"id":4418489049307132906}]}`)

    dat := make(map[string]interface{})
    d := json.NewDecoder(bytes.NewBuffer(body))
    d.UseNumber() // 启用 UseNumber 模式

    if err := d.Decode(&dat); err != nil {
        panic(err)
    }

    tags := dat["tags"].([]interface{})
    firstTagID := tags[0].(map[string]interface{})["id"]

    // 断言为 json.Number 类型
    n, ok := firstTagID.(json.Number)
    if !ok {
        panic("ID is not a json.Number")
    }

    // 将 json.Number 转换为 uint64
    // 注意:根据实际数据范围和符号选择 ParseUint 或 ParseInt
    i64, err := strconv.ParseUint(string(n), 10, 64)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Using UseNumber: Type: %T, Value: %d\n", i64, i64) // Output: Using UseNumber: Type: uint64, Value: 4418489049307132905

    secondTagID := tags[1].(map[string]interface{})["id"].(json.Number)
    i64_2, _ := strconv.ParseUint(string(secondTagID), 10, 64)
    fmt.Printf("Using UseNumber: Type: %T, Value: %d\n", i64_2, i64_2) // Output: Using UseNumber: Type: uint64, Value: 4418489049307132906
}
登录后复制

优点:

Find JSON Path Online
Find JSON Path Online

Easily find JSON paths within JSON objects using our intuitive Json Path Finder

Find JSON Path Online 30
查看详情 Find JSON Path Online
  • 提供了对数字解析的精确控制,完全避免了精度丢失。
  • 适用于 JSON 结构不完全确定或需要泛型处理的场景。

缺点:

  • 需要手动将 json.Number 转换为具体整数类型,增加了代码的复杂性。
  • 对于深层嵌套的 JSON 结构,类型断言可能变得繁琐。

解决方案二:定义具体的 Go 结构体

当 JSON 数据的结构已知时,最推荐且最符合 Go 风格的解决方案是定义一个与 JSON 结构相匹配的 Go 结构体。通过将结构体字段的类型明确声明为 uint64 或 int64,json.Unmarshal 会自动尝试将 JSON 中的数字解析为这些指定的类型,从而避免默认的 float64 转换。

实现步骤:

  1. 根据 JSON 数据的结构,定义一个或多个 Go 结构体。
  2. 将需要保留 int64 或 uint64 精度的字段声明为 uint64 或 int64 类型。
  3. 使用 json.Unmarshal 直接将 JSON 字节流解码到结构体实例中。

示例代码:

package main

import (
    "encoding/json"
    "fmt"
)

// 定义与 JSON 结构对应的 Go 结构体
type Tag struct {
    ID uint64 `json:"id"` // 将 id 字段声明为 uint64,确保精度
}

type Data struct {
    Tags []Tag `json:"tags"`
}

func main() {
    body := []byte(`{"tags":[{"id":4418489049307132905},{"id":4418489049307132906}]}`)

    var data Data
    if err := json.Unmarshal(body, &data); err != nil {
        panic(err)
    }

    fmt.Printf("Using specific struct: Type: %T, Value: %d\n", data.Tags[0].ID, data.Tags[0].ID) // Output: Using specific struct: Type: uint64, Value: 4418489049307132905
    fmt.Printf("Using specific struct: Type: %T, Value: %d\n", data.Tags[1].ID, data.Tags[1].ID) // Output: Using specific struct: Type: uint64, Value: 4418489049307132906
}
登录后复制

优点:

  • 代码更加清晰、类型安全且易于维护。
  • 利用 Go 的类型系统,减少了手动类型转换和潜在的错误。
  • 符合 Go 语言的惯用编程风格。
  • 性能通常优于泛型 map[string]interface{} 的处理。

缺点:

  • 需要提前知道 JSON 的

以上就是Go JSON 解析:避免 int64 精度丢失的两种策略的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号