จากที่เคยสัญญาไว้ว่าจะมาเขียนเรื่อง feature ใหม่ใน Go1.24.0 อันนึงที่ชื่อ omitzero ที่จะมาให้ใช้กับ package encoding/json
โดยถ้าใครเคยใช้ omitempty มาก่อน ก็จะมีวิธีใช้งานเหมือนกัน และเพื่อให้เข้าใจง่าย เรามาเขียนโค้ดทดสอบง่ายๆกันก่อน
ตัวอย่างแรก จะเป็นการสร้าง struct ขึ้นมาตัวหนึ่งหน้าตาแบบนี้
type User struct {
Name string
Title *string
DateOfBirth time.Time
Children int
}
แล้วก็มาเขียนโค้ดง่ายๆเพื่อสร้าง json message จาก struct นี้ โดยเราจะกำหนดค่าให้แค่ field เดียว
func main() {
u := User{Name: "Pallat"}
b, err := json.MarshalIndent(u, " ", " ")
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
แล้วทดลองรันด้วยคำสั่ง go run main.go
จะเห็นผลลัพธ์แบบนี้
{
"Name": "Pallat",
"Title": null,
"DateOfBirth": "0001-01-01T00:00:00Z",
"Children": 0
}
เราจะเห็นว่า บางค่าที่เราไม่ได้กำหนดค่าให้ แต่เมื่อเราสร้าง json message ออกมา เรากลับได้เห็น message ของ field นั้นพร้อมค่าบางอย่างเช่น Title เป็นค่า null เนื่องจาก type ของมันเป็น *string ซึ่งพอเป็น pointer มันก็จะแสดงคำว่า null
ออกมา และพฤติกรรมนี้แหล่ะ ที่แต่เดิม ถ้าเราไม่ต้องการจะเห็นมัน เราก็จะติด tag คำว่า omitempty
เข้าไป โดยเราจะมาทดลองติด tag นี้ให้กับทุกๆ field ดูกัน
type User struct {
Name string `json:"name,omitempty"`
Title *string `json:"title,omitempty"`
DateOfBirth time.Time `json:"dob,omitempty"`
Children int `json:"children,omitempty"`
}
ส่วนโค้ดเราก็จะเปลี่ยนเอาค่า Name ออกไปด้วยเลย จะได้เห็นพฤติกรรมของ string และ *string ด้วย
func main() {
u := User{}
b, err := json.MarshalIndent(u, " ", " ")
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
ผลลัพธ์ที่ได้จะเป็นแบบนี้
{
"dob": "0001-01-01T00:00:00Z"
}
หมายความว่าโดยปกติ แม้แต่ basic type ถ้ามันมีค่าเป็น zero value เพียงแค่เราติด tag omitempty เข้าไป เวลาที่ Marshal ออกมามันจะ omitted ค่าใน field นั้นให้เลย ซึ่งเดิม เราก็จะมาติดปัญหาตรง type ที่มันมีโครงสร้างเช่น time.Time นี่แหล่ะที่ต่อให้เราติด omitempty
เข้าไปแล้ว เราก็จะยังเห็นค่าแบบนี้อยู่ดี ทีนี้เราจะลองมาติด tag omitzero
เข้าไปเพิ่มดูแบบนี้
type User struct {
Name string `json:"name,omitempty"`
Title *string `json:"title,omitempty"`
DateOfBirth time.Time `json:"dob,omitempty,omitzero"`
Children int `json:"children,omitempty"`
}
แล้วเรารันโค้ดชุดเดิม ผลลัพธ์จะกลายเป็น
{}
ส่วนตัวผมคิดว่าบางอย่างอาจจะไม่ได้เป็นไปตาม document ยกตัวอย่างเช่นค่า int ที่ถ้าเราไม่กำหนดค่าให้ แล้วโดยปกติมันจะมี zero value เป็น 0
การติด omitempty
มันไม่ควรทำให้ค่านี้หายไป แต่ตอนนี้ถ้าเราทดลองดูก็จะเห็นว่ามันหายไปด้วยแล้ว
แต่ก็ไม่เป็นปัญหา ถ้าเราอยากเห็นเลย 0
เราก็เอา tag ออกก็ใช้ได้
ส่วนที่ doc พยายามอธิบายคือ ถ้าเราติด omitempty
มันจะดูที่ตัว message ของ json ว่าถ้าค่าที่ได้มาได้ค่่าเช่น null หรือ string, object หรือ array ที่ว่างเปล่า มันจะ omitted ให้
ส่วน omitzero
จะมีผลกับค่า zero value ของ Go เอง เช่น time.Time ที่เราไม่ได้กำหนดค่าให้มัน มันจะ omitted ให้ และจะมีผลกับค่าที่มเมธอด IsZero() bool
ด้วยเช่นกัน
ทีนี้เราลองทดลองค่าที่เป็น int กันดูว่า ถ้าเรามีเงื่อนไขให้มันเช่น ถ้าค่าที่ได้มีค่าตั้งแต่ 0
ขึ้นไป เราจะแสดงค่าเหล่านั้นออกมาใน json message และในทางกลับกันถ้าได้ค่าติดลบใดใดเราจะ omitted มัน
type User struct {
Name string `json:"name,omitempty"`
Title *string `json:"title,omitempty"`
DateOfBirth time.Time `json:"dob,omitempty,omitzero"`
Children Int `json:"children,omitzero"`
}
type Int int
func (i Int) IsZero() bool {
return i < 0
}
โดยเราสร้าง type ใหม่ที่ชื่อ Int และเอาไปใช้กับ field ชื่อ Children โดยเราใส่เงื่อนไขใน IsZero
ว่าถ้าค่าน้อยกว่า 0 จะคืนค่า false
ออกมา
แล้วเปลี่ยน tag เป็น omitzero
ดู จากนั้นเราก็รันโค้ดชุดเดิม ก็จะได้ผลลัพธ์แบบนี้
{
"children": 0
}
เอาละครับ หลักๆแล้ว เราก็ได้ของที่อำนวยความสะดวกมาให้เราเพิ่มอย่างหนึ่ง อย่างน้อยเวลาเราจะต้องสร้าง json message ที่มี type ซับซ้อน เราก็ยังมีตัวช่วยให้ชีวิตง่ายขึ้นมาอีกหน่อย แต่เพื่อความชัวร์ เวลาเขียนก็ลองทดสอบพฤติกรรมมันให้มั่นใจดูก่อนนะครับ หวังว่าจะเป็นประโยชน์
Top comments (0)