我正在尝试使用
map[string]interface{}
类型在 Go 中创建 JSON 表示。我正在处理 JSON 字符串,并且很难弄清楚如何避免 JSON 解组器自动将数字处理为 float64。结果出现以下错误。
例如。
"{ 'a' : 9223372036854775807}"
应该是 map[string]interface{} = [a 9223372036854775807
但实际上是 map[string]interface{} = [a 9.2233720368547758088E18]
我搜索了如何使用结构来通过使用
json.Number
来避免这种情况,但我真的更喜欢使用上面指定的 map
类型。
json.Unmarshal(...)
函数自动使用 float64 作为 JSON 数字。如果您想将数字解组为不同的类型,那么您必须使用自定义类型和自定义解组器。无法强制解组器将自定义值反序列化为通用映射。
big.Int
。
package main
import (
"encoding/json"
"fmt"
"math/big"
)
type MyDoc struct {
A BigA `json:"a"`
}
type BigA struct{ *big.Int }
func (a BigA) UnmarshalJSON(bs []byte) error {
_, ok := a.SetString(string(bs), 10)
if !ok {
return fmt.Errorf("invalid integer %s", bs)
}
return nil
}
func main() {
jsonstr := `{"a":9223372036854775807}`
mydoc := MyDoc{A: BigA{new(big.Int)}}
err := json.Unmarshal([]byte(jsonstr), &mydoc)
if err != nil {
panic(err)
}
fmt.Printf("OK: mydoc=%#v\n", mydoc)
// OK: mydoc=main.MyDoc{A:9223372036854775807}
}
func jsonToMap(jsonStr string) map[string]interface{} {
result := make(map[string]interface{})
json.Unmarshal([]byte(jsonStr), &result)
return result
}
@maerics 接受的答案不提供OP所需的地图输出,而是一个结构。虽然这似乎是 golang 中公认的标准方法,但结构比映射更加严格,如果 JSON 和结构完全不同步,就会破坏代码。虽然它可能使 go 代码对于 golang 执行 JSON<->struct 更具确定性和“稳定”,但它却使灵活性变得痛苦。
我也想要一个通用映射来捕获所有输入,无论 JSON 的字段是什么并且无需先验知识。制作结构需要我事先了解有关 JSON 的所有信息。对于某些用例来说,这是一个缺陷。
@Omkesh Sajjanwar 的第二个答案确实实现了这一点,但仅适用于单层/非嵌套 JSON 结构。第一层键被提取并放入映射中,但所有字段都作为接口进来,如果该字段是可接口值(int、bool、float 等),那么这很好,但如果该字段是映射,则不起作用或字符串(以我对 golang 的天真的理解)。如果 func jsonToMap(jsonStr string) map[string]interface{} 可以递归地处理嵌套映射并转换字符串,那么这将是OP问题的首选和真实答案。
[如果可以的话,我会否决已接受的答案,并对这两个答案发表评论,但可惜的是,永远被 SE 声誉所阻止。我可能会因为偏离主题或太愚蠢或一些废话而被否决。非常期待。]