Go Marshal

Go 進行資料序列化與 Marshal 名稱的起源

前言

在前端處理傳遞資料到後端時都快忘了有「序列化與反序列化資料」這個步驟,因為都被套件像是:Axios🔗 抽象掉了,近期在寫後端也重新溫習相關知識,也延續先前文章:Go Struct Tag 是什麼?如何透過 reflect 動態處理欄位? 探討 Go 如何處理序列化資料。

fetch("x", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
})
// Axios 自動序列化資料
axios.post("x", {
project_id: "<project_id>"
})

序列化與編組

什麼是序列化 Serialization?

程式語言有自己的資料結構,像是 Go 的 map 與 JavaScript 的 object 雖然外表相似但背後實踐卻完全不同,為了讓兩者相互溝通最常見就是透過 JSON 資料格式來傳遞資料。

把記憶體中的資料結構轉換成可儲存或可傳輸的格式稱為序列化(Serialization),再將其還原回原始資料結構稱為反序列化(Deserialization)。

什麼是編組 Marshal

Marshal 原意是「集結」、「編排」或「整理」。

  • 軍事: “To marshal troops”(集結部隊),代表將零散的士兵排列成有序的陣型,以便行動。
  • 法律或活動: “To marshal facts”(整理事實),代表將混亂的資訊整理成有邏輯的結構。
  • 電腦科學:被借喻為將記憶體中「散落」或「指標導向」的複雜資料結構(如一個含有指標的 Struct),整隊排列成一串可以傳輸的扁平(Flat)位元組。

Marshal 與 Serialization 的微小差異

雖然大多數情況下它們可以互換,但在電腦科學的術語定義上,兩者存在細微差別:

特性Serialization (序列化)Marshalling (編組)
核心意圖將資料轉換為持久化格式(存檔、存資料庫)。將資料轉換為傳輸格式(跨程序或跨網路通訊)。
包含內容通常只關注資料數值本身。除了資料,有時還包含元數據(Metadata),甚至涉及遠端過程調用(RPC)的處理。
結構複雜度側重於線性化。側重於將複雜、非連續的物件「整隊」成可傳輸的形式。

Go Marshal

Go 標準庫已經內建 encoding/json 套件。

  • Struct 欄位名稱會直接成為 JSON key
  • 大寫開頭的欄位才能被序列化(exported)
import (
"encoding/json"
"fmt"
)
type User struct {
Name string
Email string
}
u := User{
Name: "Riceball",
}
b, err := json.Marshal(u)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b)) // {"Name":"Riceball","Email":"[email protected]"}

JSON struct tag

通常會透過 struct tag 來控制輸出格式:

type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
// 轉換出 JSON 格式:{"name":"Riceball","email":"[email protected]"}
type User struct {
ID int `json:"id"` // 重新命名欄位
Name string `json:"name,omitempty"` // 零值時不輸出
Token string `json:"-"` // 完全忽略此欄位
}
type User struct {
ID int `json:"id"`
Name string `json:"name,omitempty"`
Token string `json:"-"`
}
var u User
err := json.Unmarshal([]byte(`{"name":"Riceball","email":"[email protected]"}`), &u)
if err != nil {
fmt.Println("出錯了:", err)
}
fmt.Printf("解析結果: %+v\n", u) // 解析結果: {ID:0 Name:Riceball Token:}

延伸閱讀