type User struct {
Age int `json:"age"`
}
func main() {
var u User
if e := json.Unmarshal([]byte(`{"age": "9223372036854775807"}`), &u); e != nil {
// panic: json: cannot unmarshal string into Go struct field User.age of type int
panic(e)
}
fmt.Printf("%+v\n", u)
}
如果不考虑兼容,最简单的改法是在tag中指定类型
type User struct {
Age int `json:"age,string"`
}
但是这样就不支持旧的数字输入了,同样会返回类型错误。
方案1:使用json.Number
type User struct {
Age json.Number `json:"age"`
}
func main() {
var u User
if e := json.Unmarshal([]byte(`{"age": "9223372036854775807"}`), &u); e != nil {
panic(e)
}
fmt.Printf("%+v\n", u) // {Age:9223372036854775807}
if e := json.Unmarshal([]byte(`{"age": 9223372036854775807}`), &u); e != nil {
panic(e)
}
fmt.Printf("%+v\n,", u) // {Age:9223372036854775807}
i, _ := u.Age.Int64()
f, _ := u.Age.Float64()
fmt.Println(i, f) // 9223372036854775807 9.223372036854776e+18 - float64溢出
var u2 User
u2.Age = json.Number("9223372036854775807")
if b, e := json.Marshal(u2); e != nil {
panic(e)
} else {
fmt.Println(string(b)) // {"age":9223372036854775807} -- js溢出
}
}
JavaScript在解析Json时,对数值类型默认时Number类型。Number是一个双精度浮点数,能够安全表示的最大整数是Number.MAX_SAFE_INTEGER,其值为9007199254740991(即2^53 - 1)。这是因为在双精度浮点数中,指数部分有11位,尾数部分有52位(加上一个隐含的位),所以能够精确表示的整数位数受限于这52位尾数。
int64类型能表示最大为2^63 - 1 (9223372036854775807),中间差距还是很大的。在64位系统上,go语言int类型其实是int64,所以很多系统倾向于将int类型转为string给到前端,前端也会把整数转成字符串给go。
go的json是强类型,在解析的时候会报错
如果不考虑兼容,最简单的改法是在tag中指定类型
但是这样就不支持旧的数字输入了,同样会返回类型错误。
方案1:使用json.Number
这种方案是标准库内置方案,它可以像js一样,同时接受string和int类型,但在读取时需要额外的方法获取int64数值;在序列化时,输出的也是Number类型,可能导致js溢出
方案2:自定义Unmarshal
go允许为自定义类型实现Unmarshal方法,因此可以实现一个自定义的类型
⚠️ Go对json中的数字也是当做float64(同样是双精度)来处理的,超过9007199254740992也会出现溢出。 这个错误是很常见的,当json.Unmarshal类型为interface{}的对象时,都会优先使用float64。
一种解决方案是不使用interface{},但是需要2次Unmarshal操作