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

Go语言JSON解析:如何避免长整型数据变为浮点数

心靈之曲
发布: 2025-11-29 14:55:01
原创
175人浏览过

Go语言JSON解析:如何避免长整型数据变为浮点数

go语言在处理json反序列化时,默认会将数字解析为`float64`类型,这可能导致大整数转换为浮点数表示,进而丢失精度或改变格式。本教程将详细介绍如何通过`json.decoder`的`usenumber()`方法,将json中的数字解析为`json.number`类型,从而有效保留其原始字符串形式,确保大整数在序列化和反序列化过程中保持精确性。

Go JSON处理中的数字类型转换问题

当使用encoding/json包进行JSON反序列化时,如果目标类型是interface{},Go语言默认会将JSON中的数字解析为float64类型。对于较小的整数,这通常不是问题。然而,当处理大整数时,float64的精度限制和其科学计数法表示可能导致原始整数值在重新序列化时发生格式变化,甚至在极端情况下丢失精度。

问题示例:

考虑一个包含整数"id": 12423434的JSON字符串。当通过json.Unmarshal将其解析到interface{}(实际为map[string]interface{})时,id的值会被隐式转换为float64类型。随后,如果再次通过json.Marshal将此map序列化回JSON字符串,float64类型的id可能会被表示为科学计数法,例如1.2423434e+07,从而改变了其原始的整数格式。

以下代码演示了这一默认行为:

立即学习go语言免费学习笔记(深入)”;

package main

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

func main() {
    // 原始JSON字符串,包含一个整数id
    var b = []byte(`
        {
        "id": 12423434,
        "Name": "Fernando"
        }
    `)

    // 将JSON反序列化到 interface{}
    var f interface{}
    json.Unmarshal(b, &f) // 此时 id 字段已被解析为 float64 类型
    m := f.(map[string]interface{})

    // 打印解析后的 map,可以看到 id 已经变为浮点数形式
    fmt.Println("解析后的map:", m)

    // 将 map 重新序列化回JSON字符串
    result, _ := json.Marshal(m) // 重新序列化时,float64 会以科学计数法表示
    fmt.Println("重新序列化后的JSON:")
    os.Stdout.Write(result)
    fmt.Println() // 添加换行,使输出更清晰
}
登录后复制

代码输出:

解析后的map: map[id:1.2423434e+07 Name:Fernando]
重新序列化后的JSON:
{"Name":"Fernando","id":1.2423434e+07}
登录后复制

从输出可以看出,整数id在经过一次反序列化和一次序列化后,从原始的整数形式变成了浮点数科学计数法形式,这可能不符合预期。

使用 json.Number 保持数字精度

为了解决上述问题,Go语言的encoding/json包提供了json.Number类型。json.Number本质上是一个字符串,它存储了JSON中数字的原始字符串表示,从而避免了在反序列化过程中将其隐式转换为float64。

当我们需要处理无法预先定义结构体的JSON数据,或者需要确保大整数的精确性时,json.Decoder的UseNumber()方法就显得尤为重要。调用UseNumber()后,json.Decoder会将所有数字解析为json.Number类型,而不是float64。

解决方案示例:

Skybox AI
Skybox AI

一键将涂鸦转为360°无缝环境贴图的AI神器

Skybox AI 140
查看详情 Skybox AI

以下代码演示了如何使用json.Decoder.UseNumber()来精确处理JSON中的数字:

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "strings"
)

var jsonData = `{
    "id": 12423434,
    "Name": "Fernando",
    "big_id": 9223372036854775807,  // 一个非常大的整数,int64 的最大值
    "decimal_val": 123.456
}`

func main() {
    // 创建一个 json.Decoder
    d := json.NewDecoder(strings.NewReader(jsonData))
    // 调用 UseNumber() 方法,指示解码器将所有数字解析为 json.Number
    d.UseNumber()

    var x interface{}
    if err := d.Decode(&x); err != nil {
        log.Fatalf("解码失败: %v", err)
    }
    fmt.Printf("解码后的数据类型和值: %#v\n", x)

    // 检查并处理解析后的字段
    if m, ok := x.(map[string]interface{}); ok {
        // 访问 id 字段
        if idVal, ok := m["id"].(json.Number); ok {
            fmt.Printf("id 字段类型为 json.Number,原始字符串: %s\n", idVal.String())
            // 可以将其转换为具体类型,例如 int64
            if i64, err := idVal.Int64(); err == nil {
                fmt.Printf("id 转换为 int64: %d\n", i64)
            } else {
                fmt.Printf("id 转换为 int64 失败: %v\n", err)
            }
        }

        // 访问 big_id 字段
        if bigIdVal, ok := m["big_id"].(json.Number); ok {
            fmt.Printf("big_id 字段类型为 json.Number,原始字符串: %s\n", bigIdVal.String())
            if i64, err := bigIdVal.Int64(); err == nil {
                fmt.Printf("big_id 转换为 int64: %d\n", i64)
            } else {
                fmt.Printf("big_id 转换为 int64 失败: %v (可能超出 int64 范围)\n", err)
            }
        }

        // 访问 decimal_val 字段
        if decVal, ok := m["decimal_val"].(json.Number); ok {
            fmt.Printf("decimal_val 字段类型为 json.Number,原始字符串: %s\n", decVal.String())
            if f64, err := decVal.Float64(); err == nil {
                fmt.Printf("decimal_val 转换为 float64: %f\n", f64)
            } else {
                fmt.Printf("decimal_val 转换为 float64 失败: %v\n", err)
            }
        }
    }

    // 将解析后的数据重新序列化
    result, err := json.Marshal(x)
    if err != nil {
        log.Fatalf("序列化失败: %v", err)
    }
    fmt.Printf("重新序列化后的JSON: %s\n", result)
}
登录后复制

代码输出:

解码后的数据类型和值: map[string]interface {}{"Name":"Fernando", "big_id":"9223372036854775807", "decimal_val":"123.456", "id":"12423434"}
id 字段类型为 json.Number,原始字符串: 12423434
id 转换为 int64: 12423434
big_id 字段类型为 json.Number,原始字符串: 9223372036854775807
big_id 转换为 int64: 9223372036854775807
decimal_val 字段类型为 json.Number,原始字符串: 123.456
decimal_val 转换为 float64: 123.456000
重新序列化后的JSON: {"Name":"Fernando","big_id":9223372036854775807,"decimal_val":123.456,"id":12423434}
登录后复制

从输出可以看出,id、big_id和decimal_val字段在解码后都被保留为json.Number类型(其内部是字符串),并在重新序列化时恢复了原始的数字形式,没有转换为浮点数科学计数法,确保了数据的精确性和格式一致性。

注意事项与最佳实践

何时使用 UseNumber()

  • 通用JSON解析: 当你无法预先定义JSON结构体,必须使用interface{}进行通用解析时,UseNumber()是确保数字精度和格式的关键。
  • 大整数处理: 当JSON中包含的整数可能超出float64的精确表示范围(例如,大于2^53的整数),或需要精确保持大整数的原始字符串形式时。
  • 数字作为字符串处理: 当你需要将JSON中的数字(特别是大整数)作为字符串进行传输或存储,而不是直接进行数值计算时。

json.Number 的处理

  • json.Number 类型提供了Int64(), Float64(), String() 等方法,方便开发者在需要时将其转换为具体的数值类型或字符串。
  • 在尝试将json.Number转换为int64或float64时,应始终检查返回的错误,以处理可能的溢出或无效转换情况。例如,一个超出int64范围的数字字符串在调用Int64()时会返回错误。

定义结构体的优势

如果JSON结构是已知的,最佳实践是定义一个Go结构体来匹配JSON的字段。这种方法提供了更好的类型安全性和代码可读性

  • 对于普通的整数,可以直接在结构体中使用int、int64等类型。encoding/json包会自动处理这些类型与JSON数字的转换。

  • 如果数字可能非常大,超出int64范围,或者确实需要保持其原始字符串形式,可以将结构体字段定义为json.Number类型。例如:

    type MyData struct {
        ID   json.Number `json:"id"`
        Name string      `json:"Name"`
        BigID json.Number `json:"big_id"`
    }
    
    func main() {
        var data MyData
        err := json.Unmarshal([]byte(jsonData), &data)
        if err != nil {
            log.Fatalf("Unmarshal failed: %v", err)
        }
        fmt.Printf("Unmarshal to struct: %+v\n", data)
        fmt.Printf("ID as string: %s, BigID as string: %s\n", data.ID.String(), data.BigID.String())
    
        // 重新序列化
        res, _ := json.Marshal(data)
        fmt.Printf("Marshal from struct: %s\n", res)
    }
    登录后复制

    这样,即使不使用json.Decoder.UseNumber(),ID和BigID字段也会被解析为json.Number,从而保留其原始形式。

总结

Go语言的encoding/json包在处理JSON数字时,默认行为是将它们解析为float64。这对于通用interface{}解析和包含大整数的场景可能导致精度问题或格式不符预期。

通过json.Decoder.UseNumber()方法,我们可以指示解码器将所有JSON数字解析为json.Number类型,从而有效保留其原始字符串形式,解决了大整数转换为浮点数的问题。在已知JSON结构的情况下,直接在结构体字段中使用json.Number类型是更明确和推荐的做法。理解并正确应用json.Number是Go语言中处理JSON大整数和保持数据精度的关键。

以上就是Go语言JSON解析:如何避免长整型数据变为浮点数的详细内容,更多请关注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号