Mình vừa đọc một bài viết hay về Struct Tags trong Golang nên mình muốn note ngay những ý quan trọng mà mình chưa biết xuống đây.
Json trong Go
Go cung cấp một standard library làm việc với json mạnh mẽ: encoding/json và struct tags tăng cường khả năng đó bằng cách cho phép tuỳ chỉnh cách dữ liệu được chuyển đổi từ data trong Go thành json và ngược lại. Trong encoding/json, ta quan tâm đến 2 hàm chính:
- Mashal: chuyển dữ liệu Go thành Json
- Unmarshal: ngược lại với marshal, chuyển Json thành dữ liệu trong Go. Struct là kiểu dữ liệu map tự nhiên với json nên thường được sử dụng để chuyển đổi thành Json và ngược lại. Lấy ví dụ ta có chương trình:
type Person struct { Name string Age int
} func main() { p := Person{Name: "Alice", Age: 30} jsonData, err := json.Marshal(p) if err != nil { fmt.Println("Error marshalling to JSON:", err) return } fmt.Println(string(jsonData))
}
Kết quả in ra:
{"Name":"Alice","Age":30}
Ta thấy các field Name, Age trong struct map tự nhiên với các key Name, Age trong jsonData.
Struct Tags
Struct Tags cho phép tuỳ chỉnh cách dữ liệu được marshal/unmarshal giữa Go data và Json.
Cũng ví dụ trên, ta muốn các key khi được chuyển thành json phải được viết thường, ta thêm các tag json:key_name
trong phần khai báo các field của struct:
type Person struct { Name string `json:"name"` Age int `json:"age"`
}
Chạy lại chương trình, ta được:
{"name":"Alice","age":30}
Các tag json:"name"
,json:"age"
chỉ ra rằng việc map field Name trong struct sẽ thành key name trong json khi marshal và ngược lại. Điều này giúp tuỳ biến cách các key được map:
package main import ( "encoding/json" "fmt"
) type Person struct { Name string `json:"ten"` Age int `json:"tuoi"` Addr string `json:"dia_chi"`
} func main() { p := Person{Name: "Alice", Age: 30, Addr: "123 Main St"} jsonData, err := json.Marshal(p) if err != nil { fmt.Println("Error marshalling to JSON:", err) return } fmt.Println(string(jsonData))
}
Chuyển struct tiếng anh thành json tiếng việt luôn.
{"ten":"Alice","tuoi":30,"dia_chi":"123 Main St"}
Omitting Fields
Field có tag json:"-"
bị bỏ qua khi marshal (tất nhiên là cũng sẽ không có key nào được map thành field này khi unmarshal).
type Person struct { Name string `json:"ten"` Age int `json:"tuoi"` Addr string `json:"-"`
}
{"ten":"Alice","tuoi":30} //Không có field Addr
Omitting Empty Fields
Tag này dùng để bỏ qua field/key có zero value. Syntax `json:"key_name,omitempty""
type Person struct { Name string `json:"ten,omitempty"` Age int `json:"tuoi,omitempty"` Addr string `json:"dia_chi"`
} func main() { p := Person{} jsonData, err := json.Marshal(p) if err != nil { fmt.Println("Error marshalling to JSON:", err) return } fmt.Println(string(jsonData)) }
Kết quả
{"dia_chi":""}
Field Name và Age có tag omitempty nên bị bỏ qua khi marshal json, Addr không có omitempty nên vẫn được giữ lại với value "".
Tạo struct hay được suggest bởi các AI assistence và tag omitempty rất thường được thêm vào một cách tự động, lưu ý hãy sử dụng tag này một cách cẩn trọng, chỉ sử dụng với những field nào nếu có value là zero value thì bỏ qua được.
Optional Fields with Pointers
Chỗ này để phân biệt zero value và unset field, dùng con trỏ cho field đó thôi. Cụ thể hơn xíu, trường hợp Alice 0 tuổi thì khi 0 được gán cho Age, Age = 0 bị hiểu là zero value và khi kết hợp với tag "omitempty", key "tuoi" sẽ không tồn tại trong json. Nhằm tránh tình trạng trên, ta dùng Age là con trỏ *int.
Marshal với embedding struct
type MetaData struct { CreatedAt string `json:"created_at,omitempty"` UpdatedAt string `json:"updated_at,omitempty"`
} type Person struct { Name string `json:"ten,omitempty"` Age int `json:"tuoi,omitempty"` MetaData //Cái này gọi là embedded field
} func main() { var user Person = Person{ Name: "Alice", Age: 30, MetaData: MetaData{ CreatedAt: "2023-10-01", UpdatedAt: "2023-10-10", }, } jsonData, err := json.Marshal(user) if err != nil { fmt.Println("Error:", err) return } err = json.Unmarshal(jsonData, &user) if err != nil { fmt.Println("Error:", err) return } fmt.Println(string(jsonData)) }
{"ten":"Alice","tuoi":30,"created_at":"2023-10-01","updated_at":"2023-10-10"}
Các filed của MetaData (là struct được nhúng trong Person) khi marshal sẽ nằm cùng cấp các các filed trong Person.
Error handling
Các operations của json hay xảy ra lỗi về tính valid của json, kiểu dữ liệu,... Để catch và handle các lỗi, bạn nên kiểm tra error sau khi thực hiện các operations:
err := json.Unmarshal(jsonData, &user) if err != nil { fmt.Println("Error:", err) return }