ทำไมภาษาจีนกลายเป็น \u4e2d\u6587
สรุปสำคัญ: การ escape แบบ \u4e2d\u6587 เป็น Unicode ที่ถูกกฎ ไม่ใช่การเข้ารหัสล้มเหลว ตัวอักษรเพี้ยนจริงหน้าตาต่างออกไป และการแยกแยะสองกรณีนี้ตัดสินว่าคุณต้องแก้อะไรจริง ๆ

สรุปสั้น ๆ: ถือว่า output \uXXXX เป็นการ escape ที่ถูกกฎ ตรวจว่า round-trip ผ่าน JSON.parse แล้ว และปิดการ serialize แบบ ASCII ล้วนอย่าง ensure_ascii=False เมื่อต้องการเห็นภาษาจีนที่อ่านได้; เก็บการ escape ไว้เฉพาะช่องทางที่ไม่สามารถบรรทุกอักขระที่ไม่ใช่ ASCII
เพราะมันถูก escape ไม่ได้เสียหาย
JSON.parse('"\\u4e2d\\u6587"') // "中文"
\u4e2d คือ 中 และ \u6587 คือ 文 ทั้งสองเป็น escape sequence ที่ถูกกฎ parser ใด ๆ คืนค่ากลับให้ ไม่มีอะไรพัง — แค่อ่านไม่ได้ใน log, diff และไฟล์ packet capture
ใครเป็นคน escape
| เครื่องมือ | ค่าเริ่มต้น | วิธีปิด |
|---|---|---|
json.dumps ของ Python |
escape (ensure_ascii=True) |
ensure_ascii=False |
JSON.stringify ของ JavaScript |
ไม่ escape | — |
| Jackson ของ Java | ไม่ escape | ไม่บังคับ |
json_encode ของ PHP |
ไม่ escape ตั้งแต่ 5.4 | — |
encoding/json ของ Go |
ไม่ escape | — |
Python เป็นแหล่งที่พบบ่อยที่สุด ค่าเริ่มต้นมีเหตุผลของมัน: output ASCII ล้วนอยู่รอดได้ทุกช่องทาง ต้นทุนคืออักขระที่ไม่ใช่ ASCII ทุกตัวกลายเป็นหกอักขระ
การ escape กับอักขระเพี้ยน
การ escape: \u4e2d\u6587 ตรงกับรูปแบบ \uXXXX เกิดจากการตั้งค่า serializer
อักขระเพี้ยน: 测试, 涓枃 เกิดจากการเขียนไบต์ UTF-8 แล้วอ่านเป็น Latin-1 หรือ GBK
ทดสอบเร็ว ๆ: วางข้อความลงใน browser console เป็น JSON.parse('"…"') ถ้าคืนค่าออกมาอ่านได้ แสดงว่าเป็นการ escape ถ้าล้มเหลวหรือยังเพี้ยนอยู่ แสดงว่าการเข้ารหัสไม่ตรงกัน — และวิธีแก้คือทำการเข้ารหัสให้ตรงกัน ไม่ใช่ไปแก้ serializer
ปิดมัน
json.dumps(data, ensure_ascii=False)
จากนั้นตรวจปลายทาง: ระบบเก่าบางตัวพึ่งพาการขนส่งแบบ ASCII ล้วนจริง ๆ โดยเฉพาะ SMS gateway และ mail relay รุ่นเก่า
เมื่อไรการ escape จึงเป็นทางเลือกที่ถูก
- ช่องทางที่เป็นปฏิปักษ์กับอักขระที่ไม่ใช่ ASCII (HTTP header รุ่นเก่า, SMS gateway บางตัว)
- JSON ที่ซ้อนอยู่ใน string ของ JSON อีกชั้น ซึ่งการ escape ลดการชนกันของเครื่องหมายคำพูด
- pipeline ของ log ที่ตัดตามไบต์และจะผ่าอักขระหลายไบต์
นอกนั้นให้เลือกความอ่านง่ายและปิดมัน
คำถามที่พบบ่อย
ทำไม Python ถึง escape ภาษาจีนโดยค่าเริ่มต้น?
เพราะ ensure_ascii=True เป็นค่าเริ่มต้น ซึ่งรับประกัน output ASCII ล้วนที่อยู่รอดได้ทุกช่องทาง ต้นทุนคืออักขระที่ไม่ใช่ ASCII ทุกตัวกลายเป็นหกอักขระ
ควรปิด ensure_ascii ไหม?
ปิดเมื่อความอ่านง่ายสำคัญ จากนั้นตรวจปลายทาง — HTTP header รุ่นเก่า, SMS gateway บางตัว และ pipeline ของ log ที่ตัดตามไบต์อาจยังพึ่งเนื้อหา ASCII ล้วน
ถ้าช่องทางบรรทุกอักขระที่ไม่ใช่ ASCII ไม่ได้จะเกิดอะไรขึ้น?
ให้เก็บการ escape ไว้ เมื่อ JSON ถูกฝังใน string ของ JSON อีกชั้น หรือเดินทางผ่านช่องทางที่เป็นปฏิปักษ์กับอักขระที่ไม่ใช่ ASCII การ escape ช่วยเลี่ยงการชนกันของเครื่องหมายคำพูดและป้องกันอักขระหลายไบต์ถูกผ่า