07/09/2018, 11:18

Golang Tag: Từ Cơ Bản đến Nâng Cao

Tag Là Gì Tag trong Golang được sử dụng để bổ sung thêm thông tin cho các field của một struct. Để hiểu rõ hơn Tag được sử dụng như thế nào và tại sao cần sử dụng chúng thì chúng ta hay bắt đầu với một số ví dụ đơn giản tạo Struct trong Golang mà không sử dụng Tag ở phần dưới đây. Struct ...

Tag Là Gì

Tag trong Golang được sử dụng để bổ sung thêm thông tin cho các field của một struct.

Để hiểu rõ hơn Tag được sử dụng như thế nào và tại sao cần sử dụng chúng thì chúng ta hay bắt đầu với một số ví dụ đơn giản tạo Struct trong Golang mà không sử dụng Tag ở phần dưới đây.

Struct Trong Golang

Nếu bạn không còn nhớ rõ Struct là gì thì đây đơn giản là một kiểu dữ liệu trong Golang bao gồm tập hợp các trường (field) và giá trị trị tương ứng của trường. Ví dụ:

package main
import "fmt"

type User struct {
    FirstName string
    LastName  string
}

func main() {
    user := User {
        FirstName: "John"
        LastName: "Doe"
    }
    fmt.Println(me.FirstName) // John
    fmt.Println(me.LastName) // Doe
}

Ở trên chúng ta định nghĩa kiểu User là một Struct với hai trường là FirstName và LastName và các trường này có kiểu dữ liệu đều là string.

Với kiểu sử dụng Struct như trên thì bạn sẽ dễ dàng liên tưởng Struct trong Golang tương tự với một mảng liên kết (associative array) trong PHP hay một object trong JavaScript hay một hash trong Ruby. Tuy nhiên thì trong Golang chúng có thể khởi tạo Struct mà không cần phải đi kèm tên trường như trên:

user := User {
    "John"
    "Doe"
}

Bạn cũng có thể viết liền một dòng như sau:

user := User {"John", "Doe"}

Ví dụ trên không có gì xa lạ với tất cả những lập trình viên nắm vững kiến thức cơ bản về Golang. Tuy nhiên bây giờ chúng ta hãy xem xem khi sử dụng tag thì có gì khác biệt.

Sử Dụng Tag trong Golang

Tag được gắn vào phía sau kiểu dữ liệu của trường trong Struct và sử dụng cú pháp key:"value", trong đó key là khoá của tag và value là giá trị của tag.

Nếu bạn từng học HTML thì Tag trong Golang khá giống với thẻ HTML và mỗi key:"value" là một thuộc tính.

Ví dụ sau đây định nghĩa một Struct có gắn tag cho các trường bên trong:

type User struct {
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
    Email     string `json:"email"`
}

Lưu ý: Một tag có thể có một hoặc nhiều cặp key:"value" thường được đặt cách nhau bởi khoảng trằng và giá trị của key có thể bỏ trống. Ví dụ:

type User struct {
    FirstName string `json:"first_name" key1:"test_value" key2 key3:""`
    LastName  string `json:"last_name"`
    Email     string `json:"email"`
}

Tuy nhiên việc thêm Tag như trên sẽ mang lại lợi ích gì? Hãy tham khảo ví dụ sau:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
    Email     string `json:"email"`
}

func main() {
    user_json_data := `
    {
        "first_name": "John",
        "last_name": "Doe",
        "email": "johndoe@example.net"
    }`

    user := new(User)
    json.Unmarshal([]byte(user_json_data), user)

    fmt.Printf("%+v
", user)
    // Hiển thị: &{FirstName:John LastName:Doe Email:johndoe@example.net}

    user_json_data_new, err := json.Marshal(user)

    fmt.Printf("%v
", err)
    // Hiển thị: <nil>

    fmt.Printf("%s
", user_json_data_new)
    // Hiển thị: {"first_name":"John","last_name":"Smith"}
}

Ở ví dụ trên method json.Unmarshal() sử dụng Tag trong User struct để truyền dữ liệu từ chuỗi JSON user_json_data vào struct tương ứng user. Ở đây bạn cần lưu ý cần sử dụng một key của tag với tên là json. Method json.Unmarshal() sẽ dựa vào giá trị của key này để map giá trị bên trong chuỗi JSON với biến user được khảo tạo từ Struct. Tương tự method json.Marshal() cũng sử dụng khoá json và giá trị tương ứng của các thẻ trong trường để parse về chuỗi json mới user_json_data_new.

Nếu bạn tò mò là json.Unmarshal() và json.Marshal() sử dụng khoá của struct như thế nào thì hãy đọc tiếp ví dụ sau:

user := User{"John", "Doe", "johndoe@example.net"}
t := reflect.TypeOf(john)

for _, fieldName := range []string{"FirstName", "LastName", "Email"} {
    field, exists := t.FieldByName(fieldName)

    if !exists {
        continue
    }

    fmt.Printf("
Field name: User.%s
", fieldName)
    fmt.Printf("	Field tag : %q
", field.Tag)
    fmt.Printf("	Value of schema tag': %q
", field.Tag.Get("schema"))
}

Ở trên bạn có thể thấy sử dụng package reflect trong Golang, chúng ta không những có thể lấy ra được tên trường trong Struct mà còn có thể lấy ra trược tên các khoá cũng như giá trị tương ứng của khoá của tag gắn với field trong Struct.

0