聊聊 Go 語言中的 JSON 序列化與 js 前端互動型別失真問題

左诗右码發表於2024-11-21

在 Web 開發中,後端與前端之間的資料交換通常透過 JSON 格式進行。

然而,在處理數字,尤其是大整數時,我們可能會遇到精度丟失的問題。這是因為 JavaScript 中的數字型別只能安全地處理一定範圍內的整數。其數字型別是基於 64 位雙精度浮點數的 Number 型別。這種型別可以安全表示 -2^532^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 資料交換中的數字精度問題。

相關文章