Go 的 struct 用來描述資料形狀:有哪些欄位、欄位是什麼型別、哪些資料應該放在一起。當資料需要存成 JSON 或透過 API 傳輸時,JSON tag 會把 Go 的欄位命名對應到外部格式。

本章目標

學完本章後,你將能夠:

  1. 定義可序列化成 JSON 的 struct
  2. 理解 omitempty 的 API 語義
  3. 分辨內部欄位命名與外部 JSON 命名
  4. 看懂設定檔、API request 與事件資料模型

【觀察】struct 用欄位集合定義資料形狀

struct 的核心規則是:每個欄位都用名稱和型別描述一部分資料。以下範例用 struct 定義一份應用設定:

1type Config struct {
2    AppName string `json:"appName"`
3    Port    int    `json:"port"`
4    Debug   bool   `json:"debug"`
5}

這段程式同時回答兩個問題:Go 程式內用哪些欄位處理設定,以及 JSON 檔案裡的欄位名稱是什麼。

【判讀】JSON tag 是外部資料格式 contract

JSON tag 的核心規則是:Go 欄位名稱服務程式碼可見性,JSON 欄位名稱服務外部資料格式。AppName 對應 appName 是兩個命名慣例的交界:

層次命名原因
Go struct 欄位AppNameexported 欄位必須大寫開頭
JSON payloadappNameJSON 與 API 常用 camelCase

omitempty 宣告「這個欄位在某些資料情境中不是必要資料」。它是可選欄位的語義標記;欄位為零值時,JSON 序列化會跳過輸出。

【策略】先用資料語意決定欄位是否必要

設計 JSON 資料時,先分辨欄位角色:

欄位角色tag 策略
每筆資料都需要不加 omitempty
只有部分情境需要omitempty
內部使用,不輸出 JSON使用 json:"-"
外部名稱需要穩定明確寫 tag,不依賴預設

這樣資料 contract 會比「把 struct 全部輸出」更清楚。

【執行】事件資料建模

事件資料模型的核心規則是:事件本身必備欄位不使用 omitempty,事件內容可依類型使用可選欄位。UserEvent 表示一筆使用者行為事件,可以來自檔案、HTTP API 或 message queue

1type UserEvent struct {
2    UserID    string    `json:"userId"`
3    Type      string    `json:"type"`
4    Timestamp time.Time `json:"timestamp"`
5    Source    string    `json:"source"`
6    Payload   Payload   `json:"payload"`
7}

這個 struct 的欄位都沒有 omitempty,表示它是事件流中的完整資料單位。相比之下,Payload 可以依事件類型使用 omitempty,因為不同事件只會填入部分欄位。

巢狀 struct

巢狀 struct 的核心規則是:資料本身有層次時,Go 型別也應保留同樣層次。以下設定把 server 相關欄位集中到 ServerConfig

 1type ServerConfig struct {
 2    Host string `json:"host"`
 3    Port int    `json:"port"`
 4}
 5
 6type Config struct {
 7    AppName string       `json:"appName"`
 8    Debug   bool         `json:"debug"`
 9    Server  ServerConfig `json:"server"`
10}

對應 JSON:

1{
2  "appName": "notify",
3  "debug": true,
4  "server": {
5    "host": "localhost",
6    "port": 8080
7  }
8}

這樣的設計讓資料層次在 Go 程式中也看得見。