如何将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
相关产品推荐
相关产品推荐

