JSON Toolkit

JSON5 และ JSONC: JSON ที่มี Comment

สรุปสำคัญ: ทั้งสองภาษาถิ่นมีอยู่เพราะ config ที่มนุษย์เขียนต้องมี comment และ trailing comma แต่ไวยากรณ์และการเข้าถึงระบบนิเวศต่างกัน เลือกผิด toolchain ก็ติดขัด

ผู้เริ่มต้นอ่านประมาณ 10 นาทีเผยแพร่ 2026-10-30อัปเดต 2026-10-30
ภาพหน้าปก JSON5 และ JSONC ซึ่งเป็น JSON ที่มี comment

สรุปสั้น: ใช้ JSONC สำหรับ comment ใน configuration ของ VS Code และ TypeScript, ใช้ JSON5 เมื่อต้องใช้ไวยากรณ์ literal ของ ES5.1 ที่กว้างกว่า เช่นเครื่องหมายคำพูดเดี่ยวหรือคีย์ที่ไม่ใส่เครื่องหมายคำพูด และเก็บ API สาธารณะไว้บน JSON มาตรฐาน

JSON มาตรฐานไม่อนุญาต comment ทุกทีมจึงเขียนมันอยู่เงียบ ๆ ต่อไป — จนวันหนึ่ง build พัง แทนที่จะทำอย่างลับ ๆ ให้เลือกภาษาถิ่นที่เหมาะสม

1. สองภาษาถิ่นมีแรงจูงใจต่างกัน

  • JSONC (JSON with Comments): JSON มาตรฐานบวก comment และ trailing comma โดยแทบไม่เปลี่ยนไวยากรณ์อื่น มันเป็นมาตรฐานโดยพฤตินัยของระบบนิเวศ VS Code — settings.json, tsconfig.json และ .eslintrc.json ต่างใช้มัน
  • JSON5: ทะเยอทะยานกว่ามาก สอดคล้องโดยตรงกับ object literal ของ ES5.1 — เครื่องหมายคำพูดเดี่ยว, คีย์ที่ไม่ใส่เครื่องหมายคำพูด, ฐานสิบหก, Infinity/NaN, การต่อบรรทัดด้วย backslash และเครื่องหมาย + นำหน้า

หนึ่งประโยคเพื่อแยกทั้งสอง: JSONC เพียงทำให้การเขียนสบายขึ้น; JSON5 ให้คุณเขียนไฟล์กำหนดค่าเสมือนเป็น JavaScript

2. ตารางเปรียบเทียบไวยากรณ์

ความสามารถ JSON มาตรฐาน JSONC JSON5
Comment // /* */ ✗ ✓ ✓
Trailing comma ✗ ✓ ✓
String ที่ใช้เครื่องหมายคำพูดเดี่ยว ✗ ✗ ✓
คีย์ที่ไม่ใส่เครื่องหมายคำพูด ✗ ✗ ✓
จำนวนฐานสิบหก ✗ ✗ ✓
จุดทศนิยมนำหน้าหรือต่อท้าย .5 5. ✗ ✗ ✓
จำนวนที่เริ่มด้วย + ✗ ✗ ✓
Infinity / NaN ✗ ✗ ✓
การต่อบรรทัดด้วย \ ท้ายบรรทัด ✗ ✗ ✓
String หลายบรรทัด ✗ ✗ ✓ (ทำผ่านการต่อบรรทัด)
ค่าระดับบนสุดอะไรก็ได้ ✓ ✓ ✓

สเปก JSON5 ทั้งหมดยาวกว่าหนึ่งหน้าเพียงเล็กน้อย; แพ็กเกจ json5 ได้รับการดูแลโดยชุมชนมายาวนาน และ browser กับ Node มีพฤติกรรมเหมือนกัน

3. การ parse: สอง API สองระดับความสามารถในการระบุตำแหน่ง

3.1 JSON5: parse ทั้งเอกสารอย่างง่าย

import JSON5 from 'json5';

const config = JSON5.parse(`
{
  // service port
  port: 8080,
  host: '0.0.0.0',
  features: ['json5', 'jsonc',],
  retries: 0x10,
}
`);

console.log(config.port, config.retries); // 8080 16

JSON5.parse คืน object ธรรมดาที่ไม่มีข้อมูล offset — เพราะมันไม่ได้พุ่งเป้าสถานการณ์แก้ไข แต่พุ่งเป้า "การอ่าน configuration" เท่านั้น

3.2 JSONC: syntax tree ที่มี offset

คุณค่าของ jsonc-parser ไม่ได้อยู่ที่การ parse แต่อยู่ที่ การแก้ไข มันมี API สามระดับ:

API จุดประสงค์ การใช้งานทั่วไป
parse Parse เป็น tree ที่พา offset/length ไฮไลต์ไวยากรณ์, folding, การนำทาง
visit เดินแบบ depth-first; callback รับ path ของ node การตรวจสอบคล้าย JSONPath
modify + applyEdits สร้างการแก้ไขข้อความขั้นต่ำ แล้วนำไปใช้กับข้อความต้นฉบับ เปลี่ยนค่าโดยรักษา comment
import { parseTree, findNodeAtLocation, modify, applyEdits } from 'jsonc-parser';

const text = `{
  // port configuration
  "server": {
    "port": 8080 // 80 in production
  }
}`;

const tree = parseTree(text);
const portNode = findNodeAtLocation(tree!, ['server', 'port']);
console.log(portNode?.offset, portNode?.length);

const edits = modify(text, ['server', 'port'], 3000, {
  formattingOptions: { insertSpaces: true, tabSize: 2 },
});
console.log(applyEdits(text, edits));

เอาต์พุตเก็บ comment เดิมไว้และแทนที่เพียงสามอักขระของ 8080:

{
  // port configuration
  "server": {
    "port": 3000 // 80 in production
  }
}

นั่นคือสิ่งที่ "แก้ไข configuration ด้วย regular expression" ทำไม่ได้: regex ไม่รู้ว่า 8080 ตัวไหนควรเปลี่ยน และไม่รู้ว่า 8080 ที่อยู่ใน string ต้องปล่อยไว้

3.3 ข้อความ error ต้องพา offset

error code ของ jsonc-parser เป็นค่า enum ตัวที่พบบ่อยคือ:

Error code ความหมาย
InvalidSymbol มีอักขระที่ไม่ควรอยู่ปรากฏขึ้น
PropertyNameExpected คาดว่าจะเจอชื่อคีย์
ColonExpected คาดว่าจะเจอ colon
CommaExpected คาดว่าจะเจอเครื่องหมายจุลภาค
CloseBraceExpected object ไม่ถูกปิด
UnexpectedEndOfComment comment ไม่ถูกปิด
InvalidEscapeCharacter escape ไม่ถูกต้อง

เมื่อแปลง offset เป็นบรรทัดและคอลัมน์ได้ editor ก็วาดเส้นหยักที่ตำแหน่งผิดพลาดได้พอดี

4. ใช้แบบไหนเมื่อใด

สถานการณ์ คำแนะนำ เหตุผล
body ของ request/response API สาธารณะ JSON มาตรฐาน ไคลเอนต์ทั่วไปไม่รองรับภาษาถิ่น
package.json, composer.json JSON มาตรฐาน package manager เข้าใจเพียง JSON มาตรฐาน
tsconfig.json, .vscode/settings.json JSONC ค่าเริ่มต้นของระบบนิเวศ รองรับโดยธรรมชาติใน toolchain
Configuration ของแอป (ต้องมี comment) JSONC หรือ YAML ต้องรักษา comment ขณะที่เครื่องมือยังอ่านและเขียนได้
ต้องใช้ Infinity, ฐานสิบหก, literal ซับซ้อน JSON5 พลังในการแสดงออกสูงสุด
การแลกเปลี่ยนข้อมูล, log, แคช รูปแบบคั่นด้วยบรรทัดหรือ JSON มาตรฐาน ต้องมี streaming และความเป็นสากล

กฎง่าย ๆ: หากไฟล์จะถูกอ่านโดย "เครื่องมือที่ไม่ใช่มนุษย์" ให้เลือก JSON มาตรฐาน; พิจารณาภาษาถิ่นเฉพาะเมื่อมั่นใจว่าโค้ดของคุณเองเท่านั้นที่อ่านมัน

5. ความเสี่ยงสามประเภทที่ภาษาถิ่นนำมา

5.1 ความต้องการล้ำขอบเขต

เมื่อเครื่องมืออื่นอ่านไฟล์กำหนดค่า JSON5 แล้วเรียก JSON.parse มัน crash ทันที อาการทั่วไปคือ "ในเครื่องใช้ได้ แต่บน Docker พัง" — เพราะ container รันเส้นทางการอ่านที่ต่างออกไป

มาตรการรับมือ: เพิ่มการตรวจใน CI ของ repository ที่ระบุชัดเจนว่าไฟล์ใดอนุญาตให้ใช้ภาษาถิ่น; ที่เหลือตรวจเป็น JSON มาตรฐาน

5.2 เซอร์ไพรส์เรื่องการอนุมานชนิด

JSON5 อนุญาตคีย์ที่ไม่ใส่เครื่องหมายคำพูดและเครื่องหมายคำพูดเดี่ยว แต่ ไม่ ให้คุณนำโครงสร้าง literal อย่างวันที่หรือ regular expression เข้ามาได้ — มันยังเป็นรูปแบบข้อมูล ไม่ใช่การประเมิน JavaScript มีคนเคยคิดว่า JSON5 แสดง new Date() ได้ แล้วพบว่า parse ล้มเหลว JSON5 ไม่ใช่ eval

5.3 Comment ถูกทิ้งอย่างเงียบ ๆ

ทั้ง JSON5.stringify และ JSON.stringify ไม่รักษา comment ดังนั้น "อ่านเข้ามา ปรับค่า แล้วเขียนกลับ" จะสูญเสีย comment ทั้งหมด เมื่อ comment ต้องอยู่รอด ทางเลือกเดียวคือ editing API ของ jsonc-parser หรือไลบรารี YAML ที่รองรับการรักษา comment

6. คำแนะนำในทางปฏิบัติ

  • [ ] เก็บตาราง "ชนิดไฟล์ → รูปแบบ" ในเอกสารของทีม เพื่อไม่ให้ใครทำอะไรตามใจ;
  • [ ] ห่อการอ่าน configuration ไว้ใน loadConfig() ตัวเดียวที่ตัดสินใจเองว่าจะใช้ parser ใด โค้ดธุรกิจจึงไม่เรียก JSON.parse โดยตรง;
  • [ ] เมื่อแก้ไข configuration ที่มี comment โดยโปรแกรม ให้ใช้ modify + applyEdits อย่าเขียนไฟล์ใหม่ทั้งหมด;
  • [ ] ตรวจไฟล์ภาษาถิ่นใน CI ด้วย (jsonc-parser สำหรับ JSONC, JSON5.parse สำหรับ JSON5) ไม่อย่างนั้น syntax error จะปรากฏเฉพาะตอนรัน;
  • [ ] ใช้ JSON มาตรฐานสำหรับอินเทอร์เฟซสาธารณะ, คีย์แคช และ payload ของ message queue

คำถามที่พบบ่อย

ควรเลือกระหว่าง JSON5 กับ JSONC อย่างไร?

เลือกตาม runtime ไม่ใช่ตามจำนวนไวยากรณ์: VS Code, TypeScript และ ESLint อ่าน JSONC เป็นค่าเริ่มต้น JSONC จึงให้ความเข้ากันได้ดีที่สุด; ใช้ JSON5 เฉพาะเมื่อต้องใช้ไวยากรณ์ literal ของ ES5.1 ที่กว้างกว่า เช่นเครื่องหมายคำพูดเดี่ยว คีย์ที่ไม่ใส่เครื่องหมายคำพูด หรือ Infinity ทั้งสองภาษาถิ่นไม่เหมาะกับ response ของ API สาธารณะ

ทำไม parser JSON มาตรฐานอ่าน JSONC โดยตรงไม่ได้?

JSONC อนุญาต comment และ trailing comma ขณะที่ JSON.parse รับเฉพาะ JSON ตาม RFC 8259 และโยน error กับทั้งสองอย่าง การอ่าน JSONC ต้องใช้ parser เช่น jsonc-parser หรือลบ comment ก่อนส่งข้อความให้ parser มาตรฐาน

Comment ใน JSONC ควรอยู่รอดหลังแก้ไขไฟล์หรือไม่?

ใช่ แต่ต้องผ่าน editing API เท่านั้น modify และ applyEdits ของ jsonc-parser เปลี่ยน node เป้าหมายโดยรักษา comment ที่เหลือ; การ parse เป็น object แล้ว stringify จะทิ้ง comment ทั้งหมด

แหล่งอ้างอิง

เครื่องมือที่เกี่ยวข้อง

บทความที่เกี่ยวข้อง

JSON Lines และ NDJSON: ข้อมูลแบบ Streaming

หนึ่งค่า JSON ต่อบรรทัดแลกมาด้วยความสามารถในการต่อท้าย การ streaming และความเสียหายที่จำกัดอยู่เพียงบรรทัดเดียว ต้นทุนคือการไม่มีโครงสร้างโดยรวม การตรวจสอบจึงต้องทำทีละบรรทัด

ระดับกลางอ่านประมาณ 10 นาทีอัปเดต 2026-10-30

การจัดการ JSON สำหรับคอนฟิก fixture และข้อมูลภายในเครื่อง

คอนฟิกต้องถูกแบ่งชั้นและตรวจสอบ; fixture ต้องแน่นอน อ่านง่าย และ diff ได้ หากไฟล์สองชนิดนี้ถูกจัดการไม่ดี ต้นทุนการวินิจฉัยปัญหาจะสูงไปอีกนาน

ผู้เริ่มต้นอ่านประมาณ 10 นาทีอัปเดต 2026-10-30

วิธีแก้ JSON parse error: ตรวจ Unexpected token ทีละขั้น

แปลง error ของ JSON.parse ให้เป็นบรรทัดและตัวอักษร แล้วตัดสาเหตุหกประการออกไป: เครื่องหมายจุลภาคท้ายรายการ เครื่องหมายคำพูดเดี่ยว คอมเมนต์ อักขระควบคุม BOM และการตัดทอนข้อมูล

ผู้เริ่มต้นอ่านประมาณ 12 นาทีอัปเดต 2026-11-01