JSON คืออะไรและทำไมมือใหม่ต้องใส่ใจเป็นพิเศษ
ถ้าคุณกำลังฝึกเขียนเว็บ คุณคงเคยได้ยินชื่อ JSON (JavaScript Object Notation - รูปแบบการเก็บข้อมูลที่อ่านง่ายทั้งคนและคอมพิวเตอร์) มาบ้างแล้ว มันเปรียบเสมือน "ภาษากลาง" ที่ใช้สื่อสารข้อมูลระหว่างเซิร์ฟเวอร์กับหน้าเว็บของเรา ไม่ว่าจะเป็นข้อมูลผู้ใช้ รายการสินค้า หรือการตั้งค่าต่างๆ ทุกอย่างล้วนถูกส่งไปมาในรูปแบบของ JSON ทั้งสิ้น
มือใหม่หลายคนมักมองว่ามันเป็นแค่ข้อความธรรมดาที่มีปีกกาครอบไว้ จึงอาจจะชะล่าใจและไม่ได้ตรวจสอบข้อมูลให้ดีก่อนใช้งานจริง แต่ในโลกของการทำงานจริง (Production) ความผิดพลาดเพียงนิดเดียวใน JSON อาจทำให้ระบบทั้งระบบล่ม หรือข้อมูลลูกค้าหายไปได้เลย ซึ่งเป็นบทเรียนราคาแพงที่โปรแกรมเมอร์หลายคนเคยเจอมากับตัว
บทความนี้จะพาไปดู 7 บั๊ก (ข้อผิดพลาดของโปรแกรม) ที่มักซ่อนตัวอยู่ใน JSON ซึ่งเราจะมาเจาะลึกกันว่าทำไมมันถึงเกิดขึ้น และเราจะมีวิธีป้องกันตัวเองอย่างไร เพื่อให้มั่นใจว่าโค้ดที่เราเขียนจะทำงานได้อย่างราบรื่นและไม่สร้างปัญหาให้เพื่อนร่วมทีมในอนาคต
1. ปัญหาตัวเลขยักษ์ใหญ่ที่ถูกตัดทอน
ปัญหาแรกที่มือใหม่มักเจอคือเรื่องของ ตัวเลข ใน JavaScript ที่มีขีดจำกัดในการเก็บค่า โดยปกติแล้ว JavaScript จะเก็บตัวเลขเป็นแบบ 64-bit float (ตัวเลขที่มีทศนิยมความละเอียดสูง) ซึ่งมีค่าสูงสุดที่ปลอดภัยคือ Number.MAX_SAFE_INTEGER หากข้อมูลที่ส่งมามีค่าเกินกว่านี้ ตัวเลขจะถูกปัดเศษแบบเนียนๆ ทำให้ค่าเปลี่ยนไปโดยที่เราไม่รู้ตัว
สถานการณ์นี้มักเกิดกับระบบที่ใช้ ID (รหัสอ้างอิง) ขนาดใหญ่ เช่น Snowflake ID (รหัสอ้างอิงที่สร้างจากเวลาและไอดีเครื่อง) ที่มักจะมีค่าเกิน 2 ยกกำลัง 53 เมื่อเราส่งค่าเหล่านี้ผ่าน JSON ตัวเลขหลักท้ายๆ จะกลายเป็นเลขศูนย์หรือเพี้ยนไปทันที ทำให้การค้นหาข้อมูลในฐานข้อมูล (Database - ที่เก็บข้อมูลหลักของระบบ) ผิดพลาดไปอย่างน่าตกใจ
วิธีแก้ที่ง่ายและได้ผลที่สุดคือการ ส่งตัวเลขขนาดใหญ่เป็น "ข้อความ" (String) แทน เพราะข้อความจะไม่มีการถูกปัดเศษโดยตัวแปลง JSON เราต้องสื่อสารกับทีม Backend (ทีมที่ดูแลระบบหลังบ้าน) ให้ปรับการส่งข้อมูลให้เป็นรูปแบบข้อความตั้งแต่ต้นทาง
// ตัวอย่างการส่งค่าที่เสี่ยงต่อการผิดพลาด
const data = JSON.parse('{"order_id": 18446744073709551615}');
console.log(data.order_id);
// ผลลัพธ์: 18446744073709552000 (เลขเพี้ยนไปแล้ว!)
// วิธีที่ถูกต้อง: ส่งเป็นข้อความ
const dataFixed = JSON.parse('{"order_id": "18446744073709551615"}');
console.log(dataFixed.order_id);
// ผลลัพธ์: "18446744073709551615" (ข้อมูลถูกต้องครบถ้วน)
โค้ดด้านบนแสดงให้เห็นว่าเมื่อเราส่งตัวเลขยาวๆ เป็น Number ตัวเลขจะถูกเปลี่ยนโดยอัตโนมัติ แต่การใส่เครื่องหมายคำพูดครอบไว้จะทำให้ JavaScript มองว่าเป็น String ทำให้ค่าคงเดิมไม่เปลี่ยนแปลง
2. ตัวอักษรล่องหนที่ทำให้ระบบพัง
บางครั้งเราได้รับข้อมูลมาจากแหล่งที่คาดไม่ถึง เช่น การก๊อปปี้ข้อความจาก Word หรือ PDF เข้ามาในฟอร์มของเว็บเรา ซึ่งมักจะมี Invisible Characters (อักขระล่องหน เช่น ช่องว่างแบบพิเศษ) ติดมาด้วย ตัวอักษรพวกนี้มองด้วยตาเปล่าไม่เห็น แต่สำหรับโปรแกรมแล้ว มันคือขยะที่ทำให้การอ่านค่า JSON ล้มเหลวทันที
เมื่อเรานำข้อมูลที่มีอักขระเหล่านี้ไปใช้คำสั่ง JSON.parse() (ฟังก์ชันแปลงข้อความ JSON ให้เป็นวัตถุในโค้ด) โปรแกรมจะฟ้องว่า SyntaxError (ข้อผิดพลาดทางไวยากรณ์) และหยุดทำงานไปเลย ซึ่งเป็นปัญหาที่หาจุดผิดได้ยากมากเพราะเรามองไม่เห็นว่าตัวปัญหาอยู่ที่ไหนในข้อความ
การป้องกันทำได้โดยการใช้ Regular Expression (นิพจน์ทั่วไป - รูปแบบการค้นหาข้อความ) เพื่อทำความสะอาดข้อมูลก่อนนำไปใช้งานเสมอ ให้สร้างฟังก์ชันสำหรับตัดอักขระที่ไม่พึงประสงค์ออกก่อนทุกครั้งที่รับค่ามาจากผู้ใช้หรือแหล่งข้อมูลภายนอก
function cleanJsonString(str) {
// ลบอักขระพิเศษที่มักติดมากับข้อความที่ก๊อปปี้มา
return str.replace(/[\u200B-\u200D\uFEFF]/g, '');
}
const rawData = '{\u200B"name": "Alex"}'; // มีตัวอักษรล่องหนอยู่ข้างหน้า
const cleanData = JSON.parse(cleanJsonString(rawData));
console.log(cleanData.name);
// ผลลัพธ์: "Alex" (โปรแกรมทำงานได้ปกติ)
ในโค้ดนี้เราใช้ .replace() เพื่อค้นหาและลบอักขระ Unicode (รูปแบบรหัสตัวอักษร) ที่มองไม่เห็นออกไปก่อนทำการแปลงข้อมูล ทำให้ JSON.parse() ไม่เกิดข้อผิดพลาด
3. กับดักเรื่องวันที่และเวลา
ในมาตรฐานของ JSON นั้นไม่มีประเภทข้อมูลแบบ Date (วันที่) โดยตรง เวลาเราส่งข้อมูลวันที่ผ่าน JSON มันจะถูกแปลงเป็นข้อความในรูปแบบ ISO 8601 (รูปแบบมาตรฐานสากล เช่น 2026-10-01T09:00:00Z) เสมอ ซึ่งเป็นปัญหาเพราะเมื่อเราแปลงกลับมา มันจะกลายเป็นแค่ข้อความธรรมดา ไม่ใช่ Object (วัตถุ) ของวันที่
เมื่อมันกลายเป็นข้อความ เราจะไม่สามารถเรียกใช้ฟังก์ชันจัดการวันที่อย่าง .getTime() หรือ .getDate() ได้เลย หากเผลอไปเรียกใช้ โค้ดของเราจะพังและขึ้นข้อความแจ้งเตือนว่าฟังก์ชันเหล่านั้นไม่มีอยู่จริง ซึ่งเป็นบั๊กที่เจอบ่อยมากเวลาดึงข้อมูลจาก API (ช่องทางเชื่อมต่อข้อมูลระหว่างโปรแกรม)
ทางแก้คือการใช้ Reviver Function (ฟังก์ชันตัวช่วยแปลงค่า) ใน JSON.parse() เพื่อตรวจสอบว่าข้อความที่ได้รับมาเป็นรูปแบบวันที่หรือไม่ ถ้าใช่ก็ให้สั่ง new Date() เพื่อแปลงกลับเป็น Object ของวันที่ให้เราโดยอัตโนมัติ
const jsonStr = '{"date": "2026-10-01T09:00:00.000Z"}';
const restored = JSON.parse(jsonStr, (key, value) => {
// ตรวจสอบว่าเป็นรูปแบบวันที่หรือไม่
if (typeof value === "string" && value.match(/^\d{4}-\d{2}-\d{2}T/)) {
return new Date(value);
}
return value;
});
console.log(restored.date instanceof Date);
// ผลลัพธ์: true (แปลงกลับเป็น Object วันที่สำเร็จ)
เราใส่ฟังก์ชันตัวที่สองเข้าไปใน JSON.parse() เพื่อตรวจสอบทุกค่าที่อ่านได้ ถ้าเจอค่าที่เป็นวันที่ให้เราสร้างใหม่เป็น Date object ทำให้ใช้งานต่อได้ทันที
4. การหายไปของข้อมูลที่ไม่ได้นิยาม
ฟังก์ชัน JSON.stringify() (คำสั่งแปลงวัตถุเป็นข้อความ JSON) มีพฤติกรรมที่น่ากลัวอย่างหนึ่งคือ มันจะ ลบข้อมูลที่ค่าเป็น undefined ทิ้งไปเลย โดยไม่บอกกล่าว นอกจากนี้ค่าอย่าง NaN (Not a Number - ค่าที่ไม่ใช่ตัวเลข) ก็จะถูกเปลี่ยนเป็น null (ค่าว่าง) ซึ่งอาจทำให้ฝั่งรับข้อมูลสับสนว่าเกิดอะไรขึ้น
สมมติว่าคุณส่งข้อมูลผู้ใช้ที่มีฟิลด์ nickname เป็น undefined พอถึงฝั่งรับ ข้อมูลนั้นจะหายไปดื้อๆ ทำให้โปรแกรมฝั่งนั้นอาจจะพังเพราะหาค่านั้นไม่เจอ หรือถ้าส่งค่าคะแนนเป็น NaN แล้วถูกเปลี่ยนเป็น null ระบบคำนวณคะแนนก็อาจจะเออเร่อได้
การป้องกันที่ดีที่สุดคือการใช้ Schema Validation (การตรวจสอบโครงสร้างข้อมูล) เช่น ไลบรารีอย่าง Zod เพื่อตรวจสอบข้อมูลก่อนส่งเสมอ วิธีนี้จะช่วยให้เรามั่นใจว่าข้อมูลทุกตัวที่จำเป็นต้องมีอยู่จริง และมีค่าที่ถูกต้องตามที่ระบบต้องการ
const user = { id: 101, nickname: undefined };
console.log(JSON.stringify(user));
// ผลลัพธ์: '{"id":101}' (nickname หายไป!)
// วิธีป้องกัน: ตรวจสอบข้อมูลก่อนส่งด้วยการใส่ค่าเริ่มต้น
const safeUser = { ...user, nickname: user.nickname || "" };
console.log(JSON.stringify(safeUser));
// ผลลัพธ์: '{"id":101,"nickname":""}' (ข้อมูลอยู่ครบ)
เราใช้การกระจาย Object (Spread operator) เพื่อกำหนดค่าเริ่มต้นให้กับตัวแปรที่อาจจะว่างเปล่า ทำให้มั่นใจว่า JSON ที่ถูกส่งออกไปจะมีฟิลด์ครบถ้วนตามต้องการ
5. เมื่อ JSON วนลูปจนระบบค้าง
บั๊กที่ร้ายแรงอีกอย่างคือ Circular Reference (การอ้างอิงแบบวนลูป) ซึ่งเกิดจากการที่ Object ตัวหนึ่งอ้างถึงอีกตัว และตัวที่สองก็ดันกลับมาอ้างถึงตัวแรกอีกที พอเราสั่งแปลง Object นี้เป็น JSON โปรแกรมจะพยายามไล่อ่านไปเรื่อยๆ จนไม่มีวันจบสิ้น และทำให้เบราว์เซอร์หรือเซิร์ฟเวอร์ค้างไปเลย
ปัญหานี้มักเกิดกับข้อมูลที่มีโครงสร้างซับซ้อน เช่น โครงสร้างต้นไม้ (Tree structure) หรือข้อมูลที่มีความสัมพันธ์กันไปมา การป้องกันต้องอาศัยการเขียนฟังก์ชันเพื่อตรวจสอบว่าเราเคยผ่าน Object นี้มาแล้วหรือยัง ถ้าเคยแล้วให้ข้ามไปหรือใส่ข้อความแทน
เราสามารถใช้ WeakSet (ชุดข้อมูลที่เก็บเฉพาะวัตถุและลบออกได้เอง) มาช่วยติดตามว่า Object ไหนถูกประมวลผลไปแล้วบ้าง เพื่อป้องกันไม่ให้เกิดการเรียกซ้ำจนระบบพัง
const parent = { name: "Engineering" };
const child = { name: "Frontend" };
parent.child = child;
child.parent = parent; // เกิดการอ้างอิงวนลูป
// JSON.stringify(parent); // จะทำให้โปรแกรมพังทันที
function safeStringify(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) return "[Circular]";
seen.add(value);
}
return value;
});
}
console.log(safeStringify(parent));
// ผลลัพธ์: {"name":"Engineering","child":{"name":"Frontend","parent":"[Circular]"}}
ฟังก์ชัน safeStringify ตรวจสอบว่า Object นี้เคยถูกประมวลผลไปแล้วหรือไม่ ถ้าใช่ให้ใส่คำว่า [Circular] แทนการวนลูป ทำให้ JSON ถูกสร้างออกมาได้โดยไม่ค้าง
สรุป: การจัดการ JSON อย่างมืออาชีพ
การเขียนโปรแกรมในระดับ Production ไม่ใช่แค่การเขียนโค้ดให้รันผ่าน แต่คือการทำให้ระบบทนทานต่อข้อมูลทุกรูปแบบ JSON อาจดูเหมือนเป็นแค่ตัวอักษรธรรมดา แต่ถ้าคุณไม่ระวังเรื่องการปัดเศษตัวเลข การมีอยู่ของอักขระแปลกปลอม หรือการวนลูปของข้อมูล คุณอาจจะเจอกับปัญหาที่แก้ได้ยากในตอนที่ระบบกำลังใช้งานจริง
สำหรับคนที่กำลังฝึกฝน ผมแนะนำให้เริ่มจากการ เขียนเทสต์ (Test - การทดสอบโค้ดโดยอัตโนมัติ) เล็กๆ น้อยๆ ทุกครั้งที่ต้องรับส่งข้อมูล JSON ลองส่งข้อมูลที่คาดว่าอาจจะพังดูว่าโค้ดเราจัดการได้ไหม ถ้าเราสร้างนิสัยการตรวจสอบข้อมูลตั้งแต่เนิ่นๆ คุณจะกลายเป็นโปรแกรมเมอร์ที่เขียนโค้ดได้นิ่งและมั่นใจกว่าใครเพื่อน
สุดท้ายนี้ เครื่องมืออย่าง JSON Formatter บนเว็บไซต์ DailyToolbox จะช่วยให้คุณตรวจสอบโครงสร้าง JSON ที่ซับซ้อนได้ง่ายขึ้น การเลือกใช้เครื่องมือที่ถูกต้องจะช่วยลดเวลาการแก้บั๊กไปได้มหาศาล ขอให้สนุกกับการพัฒนาซอฟต์แวร์และอย่าลืมว่าความละเอียดรอบคอบคือหัวใจของโปรแกรมเมอร์ที่เก่งครับ
ที่มา: JSON in Production: 7 Subtle Bugs That Break Web APIs and How to Debug Them — DEV Community