在 Web 開發中,後端與前端之間的資料交換通常透過 JSON 格式進行。
然而,在處理數字,尤其是大整數時,我們可能會遇到精度丟失的問題。這是因為 JavaScript 中的數字型別只能安全地處理一定範圍內的整數。其數字型別是基於 64 位雙精度浮點數的 Number
型別。這種型別可以安全表示 -2^53
到 2^53
之間的整數,超過這個範圍的整數將無法精確表示,但是我們後端語言的整數範圍是超過的,因此就有可能會遇到精度丟失的問題。
本文將透過 Go 語言的 encoding/json
包,探討如何透過 JSON 序列化與反序列化來避免數字精度丟失的問題。
Go 語言中的 JSON 處理
Go 語言的 encoding/json
包提供了強大的 JSON 序列化與反序列化功能。透過合理地使用結構體標籤,我們可以控制 JSON 的編碼與解碼行為。
序列化:將大整數轉為字串
在 Go 語言中,如果我們有一個大整數,比如 math.MaxInt64
,直接序列化為 JSON,那麼在 JavaScript 中可能會丟失精度。為了解決這個問題,我們可以將大整數以字串的形式序列化。
因為字串不存在精度問題,從而從側邊也就解決了數字精度的問題。
type User struct {
UserID int64 `json:"user_id,string"`
Name string `json:"name"`
Age int `json:"age"`
}
func DigitalDistortionDemo() {
data := User{
UserID: math.MaxInt64,
Name: "Alex",
Age: 18,
}
b, err := json.Marshal(data)
if err != nil {
log.Fatalf("json marshal failed: %v", err)
}
fmt.Printf("r1: %s\n", string(b))
}
在上述程式碼中,我們在 User
結構體的 UserID
欄位上使用了 json:"user_id,string"
標籤,這告訴 json.Marshal
函式將 UserID
以字串的形式序列化。
反序列化:將字串還原為大整數
當從前端接收到的 JSON 字串中的 user_id
為字串型別時,我們需要確保在反序列化過程中將其正確地轉換回大整數。
func DigitalDistortionDemo1() {
s := `{"user_id":"9223372036854775807","name":"Alex","age":18}`
var user User
if err := json.Unmarshal([]byte(s), &user); err != nil {
log.Fatalf("json unmarshal failed: %v", err)
}
fmt.Printf("r2: %+v\n", user)
}
在這段程式碼中,json.Unmarshal
函式將 JSON 字串中的 user_id
欄位正確地解析為 User
結構體中的 UserID
欄位,即使它是以字串形式提供的。
這樣也就完美解決了,我們後端的數值傳給 js 前端,前端丟失精度的問題。
並且因為 js 前端需要字串型別,而我們後端定義的型別是一個 int64 型別,透過只是加了一個 string
json tag ,從而就優雅的解決了 js 前端無論接收還是傳值都用 string,後端繼續使用 int64 型別,不用再做型別轉換問題。
結論
透過在 Go 語言中合理使用 encoding/json
包的結構體標籤,我們可以有效地避免在 JSON 序列化與反序列化過程中的數字精度丟失問題。
這種方法對於處理大整數,特別是在與 JavaScript 環境互動時,尤為重要。希望本文能夠幫助你更好地理解和應用 JSON 資料交換中的數字精度問題。