[Golang] ข้อควรระวัง map[string]interface{}

Visarut Junsone
Lazy-Dev
Published in
2 min readMar 2, 2019

กลับมาอีกครั้งกับ Golang หลังจากหันไปทำ flutter อยู่ 3 เรื่อง พอดีตอนทำงานเจอ case ของการใช้ map[string]interface{} อย่างหนึ่งที่อาจจะทำให้คน งงกันพอสมควร

ข้อควรระวังนั้นก็คือ!

การเปลี่ยนค่าภายใน map[string]interface{} ที่มีการซ้อนกันข้างในหลายชั้น เพื่อความเข้าใจก็คงต้องยกตัวอย่าง จำได้ไหมจากเรื่อง [Golang] How to parse JSON หัวข้อ Unstructured data เราได้ทำการใช้งาน map[string]interface{} เพื่อแปลง string JSON มาอยู่ในรูปแบบที่เราใช้ได้ง่ายโดยไม่ต้องมี struct แล้วถ้าเรามี JSON ในลักษณะนี้ละ

{
"Person": {
"Name": "BLABLA",
"Age": 23
},
"Position": "Full-Stack Developer",
"Main": "Back-End Developer"
}

จะเห็นว่ามี Person ที่เป็น Data แบบ ซ้อนเข้าไปอีกชั้น เอาละแปลงมันให้อยู่ในรูปของ map[string]interface{} ก่อน

str := `{
"Person": {
"Name": "BLABLA",
"Age": 23
},
"Position": "Full-Stack Developer",
"Main": "Back-End Developer"
}`
var result map[string]interface{}
json.Unmarshal([]byte(str), &result)

และเมื่อเราต้องการเปลี่ยนค่าด้านใน

str := `{
"Person": {
"Name": "BLABLA",
"Age": 23
},
"Position": "Full-Stack Developer",
"Main": "Back-End Developer"
}`
var result map[string]interface{}
json.Unmarshal([]byte(str), &result)
fmt.Println("result ========> ", result)
if p, ok := result["Person"].(map[string]interface{}); ok {
fmt.Println("p ========> ", p)
p["Name"] = "Change Name"
p["Age"] = 24
fmt.Println("p ========> ", p)
}
fmt.Println("result ========> ", result)

จาก Code ด้านบนนั้นแสดงถึงการเข้าถึงค่าเพื่อแก้ค่าด้านในทุกอย่างดูปกติใช่ไหมละครับ ผลลัพท์ที่ได้ก็จะเป็นในลักษณะแบบนี้

รูปที่ 1 แสดงผลลัพท์การเปลี่ยนค่า

จะเห็นว่า result ในตอนแรกนั้นก็จะเหมือนกับ Input ตั้งตอนของเรานั้นละ จากนั้นเราก็ทำเปลี่ยนค่า Name “BLABLA” เป็น “Change Name” และ ค่า Age 23 เป็น 24 result สุดท้ายค่าก็เปลี่ยนไปอย่างที่เราต้องการ

แต่! ถ้าเราต้องการเก็บค่า Person ให้กับอีกตัวแปรที่เราสร้างไว้แล้วทำการเปลี่ยนค่ามัน

str := `{
"Person": {
"Name": "BLABLA",
"Age": 23
},
"Position": "Full-Stack Developer",
"Main": "Back-End Developer"
}`
var result map[string]interface{}
json.Unmarshal([]byte(str), &result)
fmt.Println("result ========> ", result)
var person map[string]interface{}
if p, ok := result["Person"].(map[string]interface{}); ok {
person = p
}
person["Name"] = "LazyDev"
person["Age"] = 25
fmt.Println("person ========> ", person)
fmt.Println()
fmt.Println("result ========> ", result)
fmt.Println()

จะเกิดเหตุการณ์แบบนี้

รูปที่ 2 แสดงการเปลี่ยนค่า person

เห็นอะไรไหมครับ เมื่อเราทำการเปลี่ยนค่า person ซึ่งทั่วๆ ไป เราก็คงคิดว่าเป็นอีกตัวแปรนึงที่ไม่ได้เกี่ยวข้องกับตัวแปร result แล้ว แต่กลับกลายเป็นว่าเมื่อเราเปลี่ยนค่า Person มันส่งผลกระทบถึง result ด้วย

โดยในกรณีนี้เนี่ยต้องระวังหากคุณเลือกที่จะส่งค่า person เข้าไปทำที่ฟังก์ชั่นอื่นๆ ใดแล้วคิดว่า result นั้นค่าไม่ได้เปลี่ยนไปเป็นอย่างเดิมแล้วเกิดการคำนวนผิดในกรณีต่างๆ

ดู Code ทั้งหมดได้ที่ Github

เจอกันใหม่ Blog หน้า Bye bye

--

--

Visarut Junsone
Lazy-Dev

I’m Full-Stack Developer. ReactJS Golang C# Javascript C++ PHP and learning a lot more. Not lazy as it name. “Lazy-Dev”