DEV Community

Pallat Anchaleechamaikorn
Pallat Anchaleechamaikorn

Posted on

omitzero

จากที่เคยสัญญาไว้ว่าจะมาเขียนเรื่อง feature ใหม่ใน Go1.24.0 อันนึงที่ชื่อ omitzero ที่จะมาให้ใช้กับ package encoding/json โดยถ้าใครเคยใช้ omitempty มาก่อน ก็จะมีวิธีใช้งานเหมือนกัน และเพื่อให้เข้าใจง่าย เรามาเขียนโค้ดทดสอบง่ายๆกันก่อน
ตัวอย่างแรก จะเป็นการสร้าง struct ขึ้นมาตัวหนึ่งหน้าตาแบบนี้

type User struct {
    Name        string
    Title       *string
    DateOfBirth time.Time
    Children    int
}
Enter fullscreen mode Exit fullscreen mode

แล้วก็มาเขียนโค้ดง่ายๆเพื่อสร้าง json message จาก struct นี้ โดยเราจะกำหนดค่าให้แค่ field เดียว

func main() {
    u := User{Name: "Pallat"}
    b, err := json.MarshalIndent(u, " ", " ")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(b))
}
Enter fullscreen mode Exit fullscreen mode

แล้วทดลองรันด้วยคำสั่ง go run main.go จะเห็นผลลัพธ์แบบนี้

{
  "Name": "Pallat",
  "Title": null,
  "DateOfBirth": "0001-01-01T00:00:00Z",
  "Children": 0
 }
Enter fullscreen mode Exit fullscreen mode

เราจะเห็นว่า บางค่าที่เราไม่ได้กำหนดค่าให้ แต่เมื่อเราสร้าง 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"`
}
Enter fullscreen mode Exit fullscreen mode

ส่วนโค้ดเราก็จะเปลี่ยนเอาค่า Name ออกไปด้วยเลย จะได้เห็นพฤติกรรมของ string และ *string ด้วย

func main() {
    u := User{}
    b, err := json.MarshalIndent(u, " ", " ")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(b))
}
Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์ที่ได้จะเป็นแบบนี้

{
  "dob": "0001-01-01T00:00:00Z"
}
Enter fullscreen mode Exit fullscreen mode

หมายความว่าโดยปกติ แม้แต่ 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"`
}
Enter fullscreen mode Exit fullscreen mode

แล้วเรารันโค้ดชุดเดิม ผลลัพธ์จะกลายเป็น

{}
Enter fullscreen mode Exit fullscreen mode

ส่วนตัวผมคิดว่าบางอย่างอาจจะไม่ได้เป็นไปตาม 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
}
Enter fullscreen mode Exit fullscreen mode

โดยเราสร้าง type ใหม่ที่ชื่อ Int และเอาไปใช้กับ field ชื่อ Children โดยเราใส่เงื่อนไขใน IsZero ว่าถ้าค่าน้อยกว่า 0 จะคืนค่า false ออกมา
แล้วเปลี่ยน tag เป็น omitzero ดู จากนั้นเราก็รันโค้ดชุดเดิม ก็จะได้ผลลัพธ์แบบนี้

{
  "children": 0
}
Enter fullscreen mode Exit fullscreen mode

เอาละครับ หลักๆแล้ว เราก็ได้ของที่อำนวยความสะดวกมาให้เราเพิ่มอย่างหนึ่ง อย่างน้อยเวลาเราจะต้องสร้าง json message ที่มี type ซับซ้อน เราก็ยังมีตัวช่วยให้ชีวิตง่ายขึ้นมาอีกหน่อย แต่เพื่อความชัวร์ เวลาเขียนก็ลองทดสอบพฤติกรรมมันให้มั่นใจดูก่อนนะครับ หวังว่าจะเป็นประโยชน์

Top comments (0)