Golang中如何保持map[string]interface{}的int64值

Golang中如何保持map[string]interface{}的int64值 大家好,

我遇到了一个问题(我必须保持通用形式,因为我将此方法用于所有类型的查询):

			genericBody = map[string]interface{}{}
			dec := json.NewDecoder(genericResp.Body)
			err = dec.Decode(&genericBody)

它将这个:

{
  "data": {
    "list": [
      {
        "advertiser_id": 6868310788839309317,
        "advertiser_name": "fggddf"
      }
    ]
  },
}

转换为:

{
   "data":{
      "list":[
         {
            "advertiser_id":6868310788839309000,
            "advertiser_name":"fggddf"
         }
      ]
   },
}

这个: 6868310788839309000 变成了: 6868310788839309317

看起来它没有保留完整的 int64 值…


更多关于Golang中如何保持map[string]interface{}的int64值的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

非常感谢

更多关于Golang中如何保持map[string]interface{}的int64值的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


哇,非常感谢你的解释

这很可能是因为JSON本质上与JavaScript及其缺陷紧密耦合。

如果你需要精确的数字,请使用字符串。

json.NewDecoder

我自动回复:

		dec := json.NewDecoder(genericResp.Body)
		dec.UseNumber()
		err = dec.Decode(&genericBody)

在向客户端写入有效载荷时仍然存在问题:

{
"ads_provider_profiles": [
{
"id": 6868310788839309000,
}
]
}

image

在处理JSON中的数字时,应始终使用“大数”,在Go语言中这意味着使用 math/big 包。

big package - math/big - pkg.go.dev big package - math/big - pkg.go.dev

Package big implements arbitrary-precision arithmetic (big numbers).

否则,将它们编码为 string 类型,就无需担心IEEE浮点数转换的问题。

NobbZ:

它的缺陷

MDN Web Docs

Number.MAX_SAFE_INTEGER - JavaScript | MDN

Number.MAX_SAFE_INTEGER 静态数据属性表示 JavaScript 中的最大安全整数 (2^53 – 1)。

9007199254740991,这远小于原帖中的数字 6868310788839309317。

然而,6868310788839309317 的十六进制表示为 0x5f512445c7c00005。由于末尾的 5 前面有那么多零,这些零可以被浮点数指数部分吸收,这意味着从 int64 到 float64 再转换回来只会丢失最后的 5。

package main
import "fmt"

func main() {
	n := 6868310788839309317
	jsmaxint := 9007199254740991
	fmt.Println(jsmaxint)
	fmt.Println(n)
	fmt.Println(int64(float64(n)))
	fmt.Printf("%x\n", n)
	fmt.Printf("%x\n", int64(float64(n)))
}
9007199254740991
6868310788839309317
6868310788839309312
5f512445c7c00005
5f512445c7c00000

在Go中,当使用json.Unmarshaljson.Decodemap[string]interface{}时,JSON中的数字默认会被解析为float64类型。由于浮点数的精度限制,大整数可能会丢失精度。

解决方案:使用json.DecoderUseNumber()方法

package main

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

func main() {
	jsonStr := `{
		"data": {
			"list": [
				{
					"advertiser_id": 6868310788839309317,
					"advertiser_name": "fggddf"
				}
			]
		}
	}`

	// 方法1:使用UseNumber()
	genericBody := map[string]interface{}{}
	dec := json.NewDecoder(strings.NewReader(jsonStr))
	dec.UseNumber() // 关键:使用Number类型而不是float64
	err := dec.Decode(&genericBody)
	if err != nil {
		panic(err)
	}

	// 提取并转换数字
	data := genericBody["data"].(map[string]interface{})
	list := data["list"].([]interface{})
	item := list[0].(map[string]interface{})
	
	// 将json.Number转换为int64
	idStr := item["advertiser_id"].(json.Number)
	idInt64, err := idStr.Int64()
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("原始值: %v\n", idStr)
	fmt.Printf("int64值: %d\n", idInt64)
	fmt.Printf("完整精度: %d\n", idInt64)
}

处理嵌套结构的通用方法

func decodeWithFullPrecision(body io.Reader) (map[string]interface{}, error) {
	genericBody := map[string]interface{}{}
	dec := json.NewDecoder(body)
	dec.UseNumber()
	err := dec.Decode(&genericBody)
	if err != nil {
		return nil, err
	}
	
	// 递归处理所有数字
	processNumbers(genericBody)
	return genericBody, nil
}

func processNumbers(data interface{}) {
	switch v := data.(type) {
	case map[string]interface{}:
		for key, val := range v {
			switch val := val.(type) {
			case json.Number:
				// 尝试转换为int64,如果失败则保持为string
				if intVal, err := val.Int64(); err == nil {
					v[key] = intVal
				} else if floatVal, err := val.Float64(); err == nil {
					v[key] = floatVal
				}
			default:
				processNumbers(val)
			}
		}
	case []interface{}:
		for i, item := range v {
			processNumbers(item)
		}
	}
}

使用自定义类型保持灵活性

type FlexibleMap map[string]interface{}

func (fm *FlexibleMap) UnmarshalJSON(data []byte) error {
	var temp map[string]interface{}
	dec := json.NewDecoder(bytes.NewReader(data))
	dec.UseNumber()
	if err := dec.Decode(&temp); err != nil {
		return err
	}
	
	// 转换所有json.Number为合适的类型
	convertNumbers(temp)
	*fm = temp
	return nil
}

func convertNumbers(m map[string]interface{}) {
	for k, v := range m {
		switch val := v.(type) {
		case json.Number:
			// 尝试int64,然后float64,最后保持string
			if i, err := val.Int64(); err == nil {
				m[k] = i
			} else if f, err := val.Float64(); err == nil {
				m[k] = f
			} else {
				m[k] = val.String()
			}
		case map[string]interface{}:
			convertNumbers(val)
		case []interface{}:
			for i, item := range val {
				if subMap, ok := item.(map[string]interface{}); ok {
					convertNumbers(subMap)
				}
			}
		}
	}
}

在你的代码中直接应用

genericBody := map[string]interface{}{}
dec := json.NewDecoder(genericResp.Body)
dec.UseNumber() // 添加这一行
err = dec.Decode(&genericBody)

// 现在可以安全地提取大整数
data := genericBody["data"].(map[string]interface{})
list := data["list"].([]interface{})
for _, item := range list {
	advertiser := item.(map[string]interface{})
	if id, ok := advertiser["advertiser_id"].(json.Number); ok {
		fullInt64, _ := id.Int64()
		fmt.Printf("完整int64值: %d\n", fullInt64)
	}
}

使用dec.UseNumber()后,JSON中的数字会被解析为json.Number类型(本质上是string),然后可以通过Int64()Float64()String()方法按需转换,从而保持完整的精度。

回到顶部