Like Share Discussion Bookmark Smile

J.J. Huang   2025-03-16   Getting Started Golang 05.結構   瀏覽次數:次   DMCA.com Protection Status

Go | 什麼是帶標籤的結構?

💬 簡介

在 Go 語言中,結構型別(struct)是一種用來封裝多個資料欄位的類型。除了簡單的資料型別欄位外,Go 還支援為結構的欄位設置標籤(tags)。結構標籤是附加在結構欄位上的元資料,通常用於與外部系統(如 JSON、SQL 等)進行交互時,來指定如何序列化或反序列化資料。在這篇文章中,我們將介紹帶標籤的結構,並提供範例說明如何使用結構標籤。

圖片來源:Gophers


🔍 什麼是結構標籤?

結構標籤是附加在結構欄位上的字串,用來指定額外的元資料。標籤通常用於定義欄位在序列化或反序列化過程中的行為,最常見的應用包括 JSON 序列化和資料庫欄位對應。標籤的格式通常是一個鍵值對,並且可以指定多個選項。

結構標籤語法

結構標籤的語法格式如下:

1
FieldName Type `key:"value" key2:"value2"`
  • FieldName 是欄位名稱。

  • Type 是欄位的資料型別。

  • 標籤用反引號(`)包住,並且每個標籤由一組鍵值對組成。

  • 範例:帶標籤的結構

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package main

    import "fmt"

    type Person struct {
    Name string `json:"name"`
    Age int `json:"age"`
    }

    func main() {
    p := Person{"Alice", 30}
    fmt.Println(p)
    }

    📝 在這個範例中,Person 結構有兩個欄位 NameAge,並且為它們分別指定了 JSON 序列化的標籤 json:"name"json:"age"。這表示當我們將這個結構轉換成 JSON 時,這些欄位將被映射為 "name""age"


🛠 常見的結構標籤應用

1️⃣ JSON 標籤

最常見的結構標籤應用是 JSON 序列化。在 Go 中,encoding/json 包使用結構標籤來控制欄位在 JSON 中的名稱。

  • 範例:使用 JSON 標籤
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package main

    import (
    "encoding/json"
    "fmt"
    )

    type Person struct {
    Name string `json:"full_name"`
    Age int `json:"age"`
    }

    func main() {
    p := Person{"Alice", 30}

    jsonData, err := json.Marshal(p)
    if err != nil {
    fmt.Println("Error marshaling JSON:", err)
    return
    }

    fmt.Println(string(jsonData)) // 輸出: {"full_name":"Alice","age":30}
    }

    📝 在這個範例中,Person 結構的 Name 欄位被標註為 json:"full_name",這意味著當結構轉換為 JSON 時,Name 欄位將被轉換為 "full_name"

2️⃣ SQL 標籤

結構標籤還可以用來與資料庫欄位進行對應。在 Go 中,許多資料庫操作庫(如 gorm)使用結構標籤來映射資料庫欄位與 Go 結構欄位。

  • 範例:使用 SQL 標籤
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package main

    import "fmt"

    type Person struct {
    ID int `gorm:"primaryKey"`
    Name string `gorm:"size:100"`
    Age int `gorm:"default:18"`
    }

    func main() {
    p := Person{ID: 1, Name: "Alice", Age: 30}
    fmt.Println(p)
    }

    📝 在這個範例中,我們為 Person 結構欄位添加了 gorm 標籤。gorm:"primaryKey" 表示 ID 是主鍵,gorm:"size:100" 表示 Name 欄位的最大長度為 100gorm:"default:18" 設定了 Age 欄位的預設值。

3️⃣ 自定義標籤

除了內建的標籤庫,Go 還支持使用自定義標籤。這些標籤可以用來提供額外的元資料,並且可以在應用中進行處理。

  • 範例:使用自定義標籤
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package main

    import "fmt"

    type Person struct {
    Name string `myTag:"This is a name"`
    Age int `myTag:"This is an age"`
    }

    func main() {
    p := Person{"Alice", 30}
    fmt.Println(p)
    }

    📝 在這個範例中,我們使用了名為 myTag 的自定義標籤。這樣的標籤通常不會直接影響 Go 語言本身的行為,但可以用於其他目的,如文件生成、配置等。


🚀 應用場景

結構標籤的應用場景非常廣泛,尤其在與外部系統進行資料交互時,常常需要根據具體要求來設置結構標籤。

1️⃣ 資料序列化與反序列化

當你需要將 Go 結構轉換為 JSON、XML 或其他格式時,結構標籤非常有用。這樣你可以控制結構中的欄位如何被轉換為對應的格式。例如,在 Web 開發中,API 請求和響應通常使用 JSON 格式,這時候就需要設置適當的 JSON 標籤。

2️⃣ 資料庫操作

許多 ORM(對象關聯映射)庫(如 GORM)依賴結構標籤來定義如何將 Go 結構映射到資料庫表格中。這樣你可以指定欄位名稱、資料類型、約束條件等。

3️⃣ 配置和文件

在某些情況下,結構標籤可以用來儲存配置選項或文件說明。這對於設置自動化工具、生成程式碼或其他元資料的場景非常有用。

  • 常見的實際應用情境:
    • 資料驗證:例如,使用結構標籤來定義資料驗證規則。
    • 配置生成:某些框架可能會根據結構標籤來生成配置或進行自動化處理。
    • API 生成:可以使用自定義標籤來生成 API 文件或處理某些特定的行為
      • 範例:使用自定義標籤來進行資料驗證
        假設我們有一個結構,並且使用自定義標籤來定義一些驗證規則,然後我們可以寫一個工具來解析這些標籤並執行相應的驗證。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        package main

        import (
        "fmt"
        "reflect"
        "strings"
        )

        type Person struct {
        Name string `validate:"nonempty"`
        Age int `validate:"positive"`
        }

        func validateStruct(s interface{}) {
        val := reflect.ValueOf(s)
        if val.Kind() == reflect.Struct {
        for i := 0; i < val.NumField(); i++ {
        field := val.Field(i)
        tag := val.Type().Field(i).Tag.Get("validate")

        // 處理 nonempty 標籤
        if tag == "nonempty" && field.String() == "" {
        fmt.Println("Field", val.Type().Field(i).Name, "cannot be empty")
        }

        // 處理 positive 標籤
        if tag == "positive" && field.Int() <= 0 {
        fmt.Println("Field", val.Type().Field(i).Name, "must be positive")
        }
        }
        }
        }

        func main() {
        p := Person{"", -1}
        validateStruct(p)
        }
        輸出:
        1
        2
        Field Name cannot be empty
        Field Age must be positive

        📝 在這個例子中,validate 標籤被定義為 nonemptypositive,並且我們使用 reflect 來解析這些標籤。當 Name 是空字符串,或者 Age 是負數時,會顯示錯誤信息。
        📝 這就是一個自定義標籤的應用。標籤本身不會執行任何操作,需由程式碼(比如 validateStruct 函式)來解析和根據標籤執行具體的業務邏輯。


🔄 補充:標籤是如何工作的?

Go 本身不會自動解析這些標籤,必須通過反射(reflection)來手動解析標籤,或者通過使用一些外部框架來進行自動解析。

反射解析標籤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"reflect"
)

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}

func main() {
p := Person{"Alice", 30}
t := reflect.TypeOf(p)

for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Println(field.Name, field.Tag)
}
}

📝 這段程式碼會輸出結構中每個欄位的名稱和對應的標籤:

輸出:

1
2
Name json:"name"
Age json:"age"

通過這樣的反射機制,我們可以自動提取標籤並根據它們執行不同的邏輯。


⚠️ 注意事項

  • 標籤的格式:結構標籤應符合一定的格式,通常由鍵值對組成。標籤的格式要符合預期的解析規則。
  • 多個標籤:一個欄位可以有多個標籤,這些標籤可以用空格分開。例如,json:"name" xml:"name" db:"name"
  • 標籤不影響邏輯:結構標籤本身不會改變程式邏輯,它只是附加的元資料,用來輔助操作。作。

🎯 總結

  • 結構標籤為 Go 語言的結構欄位提供了額外的元資料。
  • 常見的應用場景包括 JSON 序列化、資料庫欄位映射和自定義配置。
  • 標籤可以幫助你更靈活地處理資料和與外部系統進行交互。

最後建議回顧一下 Go | 菜鳥教學 目錄,了解其章節內容。


註:以上參考了
Go/)