You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将JSON中的interface{}准确反序列化为int64?

解决JSON反序列化interface{}类型Id的精度丢失问题

我定义了如下Request结构体,其中Id字段类型为interface{},因为它需要接收字符串或数字类型的JSON数据:

type Request struct {
    Id   interface{} `json:"id"`
    Code string      `json:"code"`
}

当传入数字类型的id时,JSON反序列化会自动将其转为float64类型,这会导致大整数精度丢失。测试代码及问题现象如下:

func main() {
    jsonData := []byte(`{"id": 123456789123456789, "code":"123"}`)

    var request Request
    err := json.Unmarshal(jsonData, &request)
    if err != nil {
        fmt.Println("Error JSON:", err)
        return
    }

    if value, ok := request.Id.(int64); ok {
        fmt.Println("Result:", value)
    } else {
        fmt.Printf("Error. Type: %T\n", request.Id)
    }
    // 输出:Error. Type: float64

    res := int64(request.Id.(float64))
    fmt.Println(res)
    // 输出:123456789123456784. 期望结果:123456789123456789
}

核心原因

float64的有效精度仅为53位,而示例中的整数123456789123456789二进制位数超过53位,用float64存储时会丢失低位精度,直接强转int64自然得不到正确结果。

解决方案

方法一:自定义类型实现UnmarshalJSON接口

自定义ID类型,自动兼容字符串和数字格式的JSON输入,直接解析为int64避免精度损失:

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

type ID int64

func (id *ID) UnmarshalJSON(data []byte) error {
    // 先尝试解析为字符串类型的id
    var strID string
    if err := json.Unmarshal(data, &strID); err == nil {
        num, err := strconv.ParseInt(strID, 10, 64)
        if err != nil {
            return fmt.Errorf("invalid string id: %w", err)
        }
        *id = ID(num)
        return nil
    }

    // 再尝试解析为数字类型(用json.Number绕过float64)
    var numID json.Number
    if err := json.Unmarshal(data, &numID); err == nil {
        intNum, err := numID.Int64()
        if err != nil {
            return fmt.Errorf("invalid number id: %w", err)
        }
        *id = ID(intNum)
        return nil
    }

    return fmt.Errorf("unsupported id format")
}

// 修改Request结构体字段类型
type Request struct {
    Id   ID     `json:"id"`
    Code string `json:"code"`
}

测试验证:

func main() {
    // 测试数字id
    jsonData := []byte(`{"id": 123456789123456789, "code":"123"}`)
    var req Request
    if err := json.Unmarshal(jsonData, &req); err != nil {
        fmt.Println("Unmarshal error:", err)
        return
    }
    fmt.Println("数字id结果:", int64(req.Id)) // 输出:123456789123456789

    // 测试字符串id
    jsonData2 := []byte(`{"id": "987654321987654321", "code":"456"}`)
    var req2 Request
    if err := json.Unmarshal(jsonData2, &req2); err != nil {
        fmt.Println("Unmarshal error 2:", err)
        return
    }
    fmt.Println("字符串id结果:", int64(req2.Id)) // 输出:987654321987654321
}

方法二:使用json.Decoder的UseNumber选项

如果不想修改结构体字段类型,可以通过解码器配置让数字解析为json.Number,再安全转为int64:

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

func main() {
    jsonData := []byte(`{"id": 123456789123456789, "code":"123"}`)

    var req Request
    decoder := json.NewDecoder(bytes.NewReader(jsonData))
    decoder.UseNumber() // 启用后数字会被解析为json.Number而非float64
    if err := decoder.Decode(&req); err != nil {
        fmt.Println("Decode error:", err)
        return
    }

    switch val := req.Id.(type) {
    case json.Number:
        intVal, err := val.Int64()
        if err != nil {
            fmt.Println("Convert number error:", err)
            return
        }
        fmt.Println("结果:", intVal) // 输出:123456789123456789
    case string:
        intVal, err := strconv.ParseInt(val, 10, 64)
        if err != nil {
            fmt.Println("Convert string error:", err)
            return
        }
        fmt.Println("结果:", intVal)
    default:
        fmt.Printf("不支持的id类型:%T\n", val)
    }
}

内容的提问来源于stack exchange,提问作者Violetta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 21:30:01