Golang中JSON转换时的类型问题解析
Golang中JSON转换时的类型问题解析 大家好,
我想知道为什么在进行 Marshal 和 Unmarshal 操作时,数字总是被当作 float64 类型处理。
以下是我重现该错误的代码:
package main
import (
"fmt"
"encoding/json"
)
type abc struct {
Name string
Data interface{}
}
func main() {
var idUser int64 = 1
a := abc {
"Abc",
map[string]interface{}{
"id_user": idUser, // string - int64
"name": "a", // string - string
"id_a": 1, // string - int
"id_b": 1.2, // string - float
},
}
b, _ := json.Marshal(a)
// verify types
var data abc
_ = json.Unmarshal(b, &data)
values := data.Data.(map[string]interface{})
for k, v := range values{
fmt.Printf("type key: %T, type value: %T\n", k, v)
}
}
这会是 Go 语言 json 包中函数的一个 bug 吗?
在此先感谢大家。
更多关于Golang中JSON转换时的类型问题解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
由于JSON只识别浮点数,这是正确的。但是,如果你使用类型化的JSON解码器,你也可以将它们读取为 int 或 json.Number。
这或许不言而喻,但如果你提前知道字段的类型,你总是可以在结构体本身中提供类型,而不是使用 interface{}。
类似这样:
package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
p := Person{
Name: "John",
Age: 30,
}
fmt.Println(p)
}
在 Go 的 encoding/json 包中,数字被解析为 float64 类型是设计行为,而不是 bug。这是因为 JSON 规范不区分整数和浮点数,而 Go 的 interface{} 类型在解析时需要确定一个具体的数字类型。
当使用 interface{} 接收 JSON 数字时,json.Unmarshal 会默认选择 float64 类型,因为它可以无损地容纳 JSON 规范中定义的所有数字值(包括整数和浮点数)。这是为了确保不会丢失精度。
以下是验证和解决这个问题的示例:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 原始数据
data := `{"int": 42, "float": 3.14, "large": 12345678901234567890}`
var result map[string]interface{}
json.Unmarshal([]byte(data), &result)
// 默认解析为 float64
for k, v := range result {
fmt.Printf("%s: %v (type: %T)\n", k, v, v)
}
// 解决方案1:使用 json.Number
var result2 map[string]json.Number
json.Unmarshal([]byte(data), &result2)
fmt.Println("\n使用 json.Number:")
for k, v := range result2 {
fmt.Printf("%s: %v (type: %T)\n", k, v, v)
// 可以转换为具体类型
if i, err := v.Int64(); err == nil {
fmt.Printf(" -> int64: %d\n", i)
}
if f, err := v.Float64(); err == nil {
fmt.Printf(" -> float64: %f\n", f)
}
}
// 解决方案2:使用具体类型定义结构体
type MyStruct struct {
Int int64 `json:"int"`
Float float64 `json:"float"`
Large int64 `json:"large"`
}
var result3 MyStruct
json.Unmarshal([]byte(data), &result3)
fmt.Println("\n使用具体类型结构体:")
fmt.Printf("Int: %d (type: %T)\n", result3.Int, result3.Int)
fmt.Printf("Float: %f (type: %T)\n", result3.Float, result3.Float)
fmt.Printf("Large: %d (type: %T)\n", result3.Large, result3.Large)
}
输出结果:
int: 42 (type: float64)
float: 3.14 (type: float64)
large: 1.2345678901234567e+19 (type: float64)
使用 json.Number:
int: 42 (type: json.Number)
-> int64: 42
-> float64: 42.000000
float: 3.14 (type: json.Number)
-> int64: strconv.ParseInt: parsing "3.14": invalid syntax
-> float64: 3.140000
large: 12345678901234567890 (type: json.Number)
-> int64: 12345678901234567890
-> float64: 12345678901234568000.000000
使用具体类型结构体:
Int: 42 (type: int64)
Float: 3.140000 (type: float64)
Large: 12345678901234567890 (type: int64)
对于你的具体案例,可以使用以下解决方案:
package main
import (
"encoding/json"
"fmt"
)
type abc struct {
Name string
Data map[string]json.Number
}
func main() {
var idUser int64 = 1
a := abc{
"Abc",
map[string]json.Number{
"id_user": json.Number(fmt.Sprintf("%d", idUser)),
"id_a": "1",
"id_b": "1.2",
},
}
b, _ := json.Marshal(a)
var data abc
_ = json.Unmarshal(b, &data)
for k, v := range data.Data {
fmt.Printf("key: %s, value: %v\n", k, v)
// 根据需要转换为具体类型
if i, err := v.Int64(); err == nil {
fmt.Printf(" -> int64: %d\n", i)
}
if f, err := v.Float64(); err == nil {
fmt.Printf(" -> float64: %f\n", f)
}
}
}
这种设计确保了 JSON 数字可以正确解析而不会丢失精度,同时提供了 json.Number 类型来延迟数字类型的转换决策。

