encoding/json
JSON ใน Go
หัวข้อที่มีชื่อว่า “JSON ใน Go”Package encoding/json แปลงค่า Go เป็น JSON text (marshaling) และแปลง JSON text กลับเป็นค่า Go (unmarshaling) ฟังก์ชันหลักสองตัวคือ:
data, err := json.Marshal(v) // Go value → []byte (JSON)err = json.Unmarshal(data, &v) // []byte (JSON) → Go valueMarshal รับค่าใดก็ได้และคืน []byte Unmarshal เขียนลงในค่าที่ชี้โดย argument ที่สอง — ส่ง pointer เสมอเพื่อให้ฟังก์ชันแก้ไขตัวแปรได้
Struct tags
หัวข้อที่มีชื่อว่า “Struct tags”Struct tag คือ raw-string literal บน field ของ struct ที่ให้ metadata Package encoding/json อ่าน tag json:"name" เพื่อควบคุมการ serialize:
type User struct { Name string `json:"name"` Email string `json:"email,omitempty"` Age int `json:"age"`}syntax ของ tag คือ `json:"<name>[,options]"` option ที่พบบ่อย:
| Tag | ผล |
|---|---|
json:"name" | ใช้ name เป็น JSON key แทนชื่อ field |
json:"name,omitempty" | ละเว้น field นี้ใน output เมื่อค่าเป็น zero value |
json:"-" | ละเว้น field นี้เสมอ |
Nested structs
หัวข้อที่มีชื่อว่า “Nested structs”Field ของ struct สามารถเป็น struct ได้เช่นกัน json.Marshal จะ recurse เข้าไปโดยอัตโนมัติ:
type Address struct { City string `json:"city"` Country string `json:"country"`}
type User struct { Name string `json:"name"` Age int `json:"age"` Address Address `json:"address"`}JSON ที่ได้จะฝัง address object ไว้ภายใต้ key "address"
map[string]any — การ unmarshal แบบยืดหยุ่น
หัวข้อที่มีชื่อว่า “map[string]any — การ unmarshal แบบยืดหยุ่น”เมื่อยังไม่มี struct type ล่วงหน้า ให้ unmarshal ลงใน map[string]any แต่ละค่า JSON จะกลายเป็น Go counterpart ที่เป็นธรรมชาติ: string, float64, bool, []any, map[string]any หรือ nil
var m map[string]anyjson.Unmarshal([]byte(`{"lang":"Go","version":1.22}`), &m)fmt.Println(m["lang"]) // Gofmt.Println(m["version"]) // 1.22 (float64)package main
import ( "encoding/json" "fmt")
type Address struct { City string `json:"city"` Country string `json:"country"`}
type User struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email,omitempty"` Address Address `json:"address"`}
func main() { // Marshal a struct — Email is set, so it appears u := User{ Name: "Alice", Age: 30, Address: Address{ City: "Bangkok", Country: "Thailand", }, } data, err := json.Marshal(u) if err != nil { fmt.Println("marshal error:", err) return } fmt.Println(string(data))
// omitempty: Email is empty, so it is omitted u2 := User{Name: "Bob", Age: 25, Address: Address{City: "London", Country: "UK"}} data2, _ := json.Marshal(u2) fmt.Println(string(data2))
// Unmarshal back into a struct var result User err = json.Unmarshal(data, &result) if err != nil { fmt.Println("unmarshal error:", err) return } fmt.Printf("Name: %s, City: %s\n", result.Name, result.Address.City)
// map[string]any — flexible unmarshaling raw := `{"lang":"Go","version":1.22,"stable":true}` var m map[string]any _ = json.Unmarshal([]byte(raw), &m) fmt.Println(m["lang"], m["version"], m["stable"])}Loading Go runtime (first run only, ~8 MB)…
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| สิ่งที่ได้ | ประโยชน์ | ต้นทุน |
|---|---|---|
| json.Marshal/Unmarshal | ง่าย, ไม่ต้อง schema | reflect-based — ช้ากว่า code-gen 2-5x |
| struct tags (json:“name”) | control field names, omitempty | runtime-only — ไม่มี compile-time check ว่า tag ถูกต้อง |
| json.Decoder (streaming) | decode large/stream JSON โดยไม่ load ทั้งหมดใน memory | API ซับซ้อนกว่า Unmarshal |
| omitempty | ไม่ส่ง zero value ใน JSON | 0, false, "" ถือเป็น empty — อาจ surprise |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- json.Marshal/Unmarshal thread-safe — ใช่ — แต่ json.Decoder/Encoder แต่ละ instance ไม่ thread-safe อย่าแชร์ข้าม goroutine
- unexported field ถูก marshal — ไม่ — unexported field ถูก ignore โดย encoding/json ไม่ว่าจะมี tag หรือไม่
- omitempty ทำงานกับ pointer เหมือน value — omitempty บน *T: nil → omit, non-nil → include แม้ว่า value ข้างใน pointer เป็น zero
- interface{} field ใน struct marshal เป็น null เสมอ — marshal ตาม concrete type ที่ store อยู่ใน interface ตอน runtime
💡 ตัวอย่างจากของจริง
Kubernetes API: ทุก Kubernetes resource (Pod, Deployment, Service) marshal/unmarshal ผ่าน encoding/json — struct tags ควบคุม JSON key ทั้งหมด
Go REST API: json.NewDecoder(r.Body).Decode(&req) คือ pattern มาตรฐานที่ทุก Go API server ใช้สำหรับ parse request body
Prometheus: /metrics endpoint ใช้ custom text format แต่ HTTP config และ rule file ใช้ json.Unmarshal สำหรับ admin API