JSON Toolkit

সিনট্যাক্স ও ফরম্যাট FAQ: কমেন্ট, ট্রেইলিং কমা ও নির্ভুলতা

সংক্ষেপে: কমেন্ট অনুমোদিত কি না, ট্রেইলিং কমা কেন এরর, কি কোটে মোড়াতে হয় কি না, 9007199254740993 কেন বদলায় — বারোটি উপসংহার, প্রতিটি RFC 8259-এর বিরুদ্ধে যাচাইকৃত।

শিক্ষানবিশ~12 মিনিট পড়াপ্রকাশ 2026-10-30আপডেট 2026-10-30
সিনট্যাক্স ও ফরম্যাট FAQ-র কভার

মূল কথা: প্রসেস সীমানা পার হওয়া যেকোনো কিছুর জন্য সাধারণ RFC 8259 JSON লিখুন — ডাবল কোট, কমেন্ট নেই, ট্রেইলিং কমা নেই, BOM ছাড়া UTF-8; কমেন্ট দরকারি কনফিগে JSONC বা JSON5 ব্যবহার করে রানটাইমে ফেরত রূপান্তর করুন; আর সার্ভারে 2^53-1-এর বেশি ইন্টিজার স্ট্রিং হিসেবে সিরিয়ালাইজ করুন।

এসব প্রশ্নের সাধারণ দিক: দেখতে "কাজ করা উচিত", কিন্তু স্পেসিফিকেশন আসলে খুব কঠোর।

১. কী বৈধ JSON হিসেবে গোনা হয়

JSON, JSON5 ও JSONC-এর পার্থক্য কী?

স্ট্যান্ডার্ড JSON কেবল ডাবল-কোটেড স্ট্রিং চেনে এবং কমেন্ট বা ট্রেইলিং কমা অনুমোদন করে না। JSON5 ও JSONC — দুইটিই এর উপরে বৈশিষ্ট্য যোগ করে:

বৈশিষ্ট্য JSON (RFC 8259) JSONC JSON5
ডাবল-কোটেড স্ট্রিং সমর্থিত সমর্থিত সমর্থিত
সিঙ্গেল-কোটেড স্ট্রিং সমর্থিত নয় সমর্থিত নয় সমর্থিত
কমেন্ট সমর্থিত নয় সমর্থিত সমর্থিত
ট্রেইলিং কমা সমর্থিত নয় সমর্থিত নয় সমর্থিত
আনকোয়োটেড কি সমর্থিত নয় সমর্থিত নয় সমর্থিত
হেক্সাডেসিমেল সংখ্যা সমর্থিত নয় সমর্থিত নয় সমর্থিত
NaN / Infinity সমর্থিত নয় সমর্থিত নয় সমর্থিত
ফাইল এক্সটেনশন .json .jsonc .json5

বাছাইয়ের নীতি সরল: প্রসেস সীমানা জুড়ে সবসময় স্ট্যান্ডার্ড JSON ব্যবহার করুন, অথচ কনফিগারেশন ফাইলে সম্পাদক কী সমর্থন করে তার উপর নির্ভর করে JSONC বা JSON5 ব্যবহার করা যায়। tsconfig.json ও .vscode/settings.json-এ কমেন্ট থাকতে পারে কারণ সম্পাদক সেগুলো JSONC হিসেবে পার্স করে; কঠোর JSON.parse-এ দিলে তবু এরর হয়।

JSON-এ কমেন্ট থাকতে পারে?

না। RFC 8259-এর ব্যাকরণে কমেন্ট উৎপাদন নেই, তাই যেকোনো // বা /* */ পার্সিং ব্যর্থ করে।

কনফিগারেশনে ব্যাখ্যা রাখতে চাইলে তিনটি বিকল্প:

{
  "_comment": "নিচের টাইমআউট একক মিলিসেকেন্ড",
  "timeout": 3000,
  "retry": {
    "$doc": "পুনরায় চেষ্টার সংখ্যা, 0 মানে পুনরায় চেষ্টা নেই",
    "count": 3
  }
}
  • _comment-এর মতো প্রথাগত ফিল্ড ব্যবহার করুন যা পাঠক কেবল উপেক্ষা করে;
  • ব্যবসায়িক ফিল্ডের সঙ্গে সংঘর্ষের সম্ভাবনা কমাতে $-প্রিফিক্সযুক্ত ফিল্ড ব্যবহার করুন;
  • কনফিগে সোজা JSONC বা YAML-এ যান আর রানটাইমে স্ট্যান্ডার্ড JSON-এ রূপান্তর করুন।

ট্রেইলিং কমা কেন এরর ঘটায়?

কারণ ব্যাকরণে অ্যারে ও অবজেক্ট — দুটোই "এলিমেন্ট + বিভাজক" কাঠামো, আর শেষ এলিমেন্টের পরে কমা অনুমোদিত নয়।

{
  "a": 1,
  "b": 2,
}

উপরের অংশে }-এর আগে বাড়তি কমা আছে, আর এরর সাধারণত কমায় বা ক্লোজিং ব্রেসে রিপোর্ট হয়, পার্সার বাস্তবায়নের উপর নির্ভর করে। JavaScript থেকে মাইগ্রেশনে এটাই সবচেয়ে সহজ ফাঁদ — JS অবজেক্ট লিটেরাল ট্রেইলিং কমা অনুমোদন করে, JSON করে না।

সিঙ্গেল কোট অনুমোদিত?

না। স্ট্রিং অবশ্যই ডাবল কোট ব্যবহার করবে; 'text' সিনট্যাক্স এরর।

ডেটা Python-এর repr(), শেল স্ক্রিপ্ট বা হাতে লেখা কনফিগ থেকে কপি হলে আগে টুল দিয়ে রূপান্তর করুন, পার্সার শিথিল হবে আশা না করে।

কি কোট ছাড়া চলতে পারে?

না। অবজেক্ট সদস্যের নাম অবশ্যই ডাবল কোটে মোড়া স্ট্রিং হতে হবে।

{ name: "json" }

এটি JavaScript অবজেক্ট লিটেরাল, JSON নয়। কোট যোগ করলে বৈধ হয়:

{ "name": "json" }

টপ লেভেল অ্যারে বা স্ট্রিং হতে পারে?

হ্যাঁ। RFC 8259 টপ লেভেলে যেকোনো JSON মান অনুমোদন করে: অবজেক্ট, অ্যারে, স্ট্রিং, সংখ্যা, true, false ও null — সবই বৈধ।

ইঞ্জিনিয়ারিং অনুশীলনে অবশ্য টপ লেভেলে কেবল অবজেক্ট বা অ্যারে ব্যবহারের সুপারিশ করা হয়, দুই কারণে:

  1. সামঞ্জস্য: পুরোনো পার্সার (RFC 4627 যুগের) টপ লেভেলে অবজেক্ট বা অ্যারে দাবি করে;
  2. নিরাপত্তা: টপ-লেভেল অ্যারেতে ঐতিহাসিকভাবে JSON হাইজ্যাকিং ঝুঁকি ছিল। আধুনিক ব্রাউজার ঠিক করেছে, কিন্তু পুরোনো সিস্টেম তবু প্রথা ধরে নেয়।

তাই "hello" বৈধ JSON আর ["a", "b"]-ও, কিন্তু {"data": [...]} ফেরত দেওয়া API নিরাপদ।

২. এনকোডিং ও স্ট্রিং

ফাইলের শুরুতে BOM পার্সিং ভাঙে?

হ্যাঁ। RFC 8259 স্পষ্টভাবে JSON টেক্সট UTF-8-এ পাঠাতে বলে এবং byte order mark (BOM) যোগ করা যাবে না।

BOM হলো ফাইলের একেবারে শুরুতে তিন বাইট EF BB BF। এটি সম্পূর্ণ অদৃশ্য, কিন্তু স্ট্রিংয়ের প্রথম অবস্থান দখল করে, তাই পার্সার ধরে "প্রথম অক্ষর { নয়" আর এরর দেয় — আর রিপোর্ট করা লাইন ও কলাম সাধারণত লাইন ১, কলাম ১, যা দেখে বিভ্রান্তি হয়।

Windows Notepad-এর "Save as UTF-8" ডিফল্টে BOM রাখে। যাচাই ও সরাতে:

# Check: output starting with efbbbf means a BOM is present
head -c 3 data.json | xxd

# Remove: skip the first 3 bytes with tail
tail -c +4 data.json > clean.json

আধুনিক JavaScript-এর JSON.parse BOM সহন করে (V8 অভ্যন্তরীণভাবে এড়িয়ে যায়), কিন্তু Java, Go ও Python-এর কঠোর বাস্তবায়ন করে না, যা "একই ফাইল ভিন্ন ভাষায় ভিন্ন ফল" হওয়ার সাধারণ কারণ।

অক্ষর কি \uXXXX-এ রূপান্তর করতে হয়?

না। RFC 8259 JSON টেক্সট UTF-8-এ চায়, তাই অক্ষর সোজা আসতে পারে; {"name": "করিম"} সম্পূর্ণ বৈধ।

\uXXXX নিছক সমতুল্য বিকল্প বানান, যে চ্যানেল অক্ষর সরাসরি প্রকাশ করতে পারে না সেখানে ব্যবহৃত। হাতের নিয়ম:

  • HTTP রিকোয়েস্ট বডি, ফাইল, ডেটাবেস: অক্ষর সোজা লিখুন, প্রতিটি অক্ষরে ৩ বাইট বাঁচে;
  • HTTP হেডার, URL প্যারামিটার, অন্য স্ট্রিংয়ের ভেতরে এম্বেড: এনকোডিং সমস্যা এড়াতে এস্কেপ ব্যবহার করুন।

যে অক্ষরগুলো অবশ্যই এস্কেপ করতে হবে তা কন্ট্রোল ক্যারেক্টার (U+0000 থেকে U+001F)। যেমন নিউলাইন লিখতে হবে \n; স্ট্রিংয়ের ভেতরে আক্ষরিক নিউলাইন রাখা যায় না।

ইমোজি কি সোজা স্ট্রিংয়ে যেতে পারে?

হ্যাঁ, তবে মনে রাখুন এটি দুই UTF-16 কোড একক নেয়।

ইমোজি ও অনেক বিরল অক্ষর (যেমন 𠮷) BMP-এর বাইরে, তাই JavaScript-এ "😀".length === 2। তাই অক্ষর-গণনা দিয়ে যাচাই করা সহজেই ভুল হয়:

const text = "😀";
console.log(text.length);          // 2, number of UTF-16 code units
console.log([...text].length);     // 1, number of code points
console.log(Buffer.byteLength(text)); // 4, number of UTF-8 bytes

ডাকনামের দৈর্ঘ্য সীমা .length দিয়ে হিসাব করলে একটি ইমোজি দুই স্লট নেয়; ব্যাকএন্ড বাইট গুনলে তা চার। সংজ্ঞা মেলানোর সময় স্পষ্ট করুন আপনি কোড পয়েন্ট, কোড একক, নাকি বাইট বোঝাচ্ছেন।

৩. সংখ্যা ও বিশেষ মান

বড় ইন্টিজার 9007199254740993 হয়ে গেল 9007199254740992 কেন?

কারণ JavaScript-এর JSON.parse সংখ্যাকে IEEE 754 ডাবল-প্রিসিশন ফ্লোতে বদলায়, আর সবচেয়ে বড় নিরাপদ ইন্টিজার 2^53-1।

Number.MAX_SAFE_INTEGER === 9007199254740991  // true
9007199254740993                              // actually yields 9007199254740992

2^53-1-এর উপরের যেকোনো সংখ্যা শেষ অঙ্কে রাউন্ড হতে পারে। সাধারণ শিকার হলো snowflake অ্যালগরিদমে তৈরি ১৯-অঙ্কের ইউজার ID, অর্ডার নম্বর, আর ন্যানোসেকেন্ড টাইমস্ট্যাম্প।

তিন সমাধান, পছন্দের ক্রমে:

  1. সার্ভারে ID স্ট্রিং হিসেবে সিরিয়ালাইজ করুন: "id": "9007199254740993", নির্ভুলতার সমস্যা শিকড়েই সরিয়ে দেয়;
  2. লস্লেস পার্সিং লাইব্রেরি ব্যবহার করুন: JSON.parse-এর প্রতিস্থাপন বড় ইন্টিজার হুবহু রাখতে পারে, যেমন json-bigint;
  3. কেবল প্রদর্শনের জন্য রেগুলার এক্সপ্রেশন দিয়ে বের করুন: পুরো ডকুমেন্ট পার্স না করে লক্ষ্য ফিল্ডকে টেক্সট হিসেবে মেলান।

খেয়াল করুন JSON.stringify-এর প্রতিসম সমস্যা আছে: নিরাপদ ইন্টিজার রেঞ্জের বাইরের সংখ্যা সিরিয়ালাইজেশনের পর আর মূল মান ধরে নাও রাখতে পারে।

NaN ও Infinity কি JSON-এ যেতে পারে?

না। JSON সংখ্যার উৎপাদন কেবল দশমিক নোটেশন অনুমোদন করে, আর NaN, Infinity, -Infinity বৈধ JSON মান নয়।

JavaScript-এর পথ "নীরব অবনতি":

JSON.stringify({ value: NaN });        // '{"value":null}'
JSON.stringify({ value: Infinity });   // '{"value":null}'

এরর নেই, NaN সোজা null হয়ে যায়। "ফ্রন্টএন্ড NaN পাঠাল আর ব্যাকএন্ড null পেল" — এই ক্লাসিক দৃশ্য এখানেই। এমন সেমান্টিক্স বোঝাতে হলে স্ট্রিং বা সম্মত সেন্টিনেল মান দিয়ে স্পষ্টভাবে প্রকাশ করুন:

{ "ratio": null, "ratioState": "not-a-number" }

সংখ্যা কি 1e3 বা 0.5 হিসেবে লেখা যায়?

হ্যাঁ। JSON সংখ্যা দশমিক ও সূচক সমর্থন করে, তাই 1e3, 1E3, 0.5 ও -0.5 — সবই বৈধ।

কিন্তু কিছু বিধিনিষেধ:

  • . দিয়ে শুরু বা শেষ হতে পারে না: .5 ও 5. — দুইটিই অবৈধ, তাই 0.5 ও 5 লিখুন;
  • শুরুতে শূন্য নয়: 01 অবৈধ;
  • হেক্সাডেসিমেল নয়: 0x1F অবৈধ, তা JSON5 এক্সটেনশন;
  • -0 বৈধ, কিন্তু JSON.stringify(-0) আউটপুট 0, চিহ্ন হারায়।

৪. অবজেক্ট সেমান্টিক্স

ডুপ্লিকেট কি-তে কী হয়?

আচরণ বাস্তবায়ন-নির্ধারিত; RFC 8259 কেবল বলে কি-নাম "should" অনন্য, আর ডুপ্লিকেটে কী হয় তা নির্ধারণ করে না।

বাস্তবে সিংহভাগ বাস্তবায়ন শেষ মান নেয়:

JSON.parse('{"a":1,"a":2}')  // { a: 2 }

কিন্তু এর উপর নির্ভর করা যায় না। কিছু Java লাইব্রেরি, Go-র encoding/json ও Python-র json সংস্করণভেদে আলাদা: কিছু প্রথম মান নেয়, কিছু এরর দেয়, কিছু শেষটি। ডুপ্লিকেট কি-কে এরর ধরাই ভাষা জুড়ে নিরাপদ পথ।

চোখে ডুপ্লিকেট কি খুঁজে পাওয়া যায় না — মিনিফাইড JSON-এ একই কি হাজার হাজার অক্ষর দূরে থাকতে পারে। স্ক্রিপ্ট ব্যবহার করুন:

# Extract keys that appear more than once at the same level, as a rough check
jq -r 'paths(scalars) | join(".")' data.json | sort | uniq -d

undefined কী হয়ে যায়?

নির্ভর করে, আর এটাই সবচেয়ে বাগপ্রবণ জায়গা:

JSON.stringify({ a: undefined });              // '{}'  the key is dropped entirely
JSON.stringify({ a: undefined, b: 1 });        // '{"b":1}'
JSON.stringify([undefined]);                   // '[null]'  becomes null inside arrays
JSON.stringify({ a: function () {} });         // '{}'  functions are dropped too

একই undefined অবজেক্টের ভেতরে "ফিল্ড উধাও" আর অ্যারেতে "null হয়ে যায়"। ব্যাকএন্ড "ফিল্ড নেই" ও "ফিল্ড null" — দুই ক্ষেত্রে আলাদা শাখা নিলে এখানেই অসামঞ্জস্য পাবেন।

"কোনো মান নেই" স্পষ্টভাবে প্রকাশ করতে হলে undefined নয়, null ব্যবহার করুন।

{"__proto__": {...}} পার্স করার পর কী হয়?

JSON.parse নিজে নিরাপদ: __proto__ সাধারণ নিজস্ব প্রপার্টি হিসেবে ধরা হয় আর প্রোটোটাইপ বদলায় না।

বিপজ্জনক হলো পার্সের পরে মার্জ অপারেশন:

const patch = JSON.parse('{"__proto__": {"isAdmin": true}}');
Object.assign({}, patch);       // some implementations pollute the prototype

সঠিক পথ হলো নিজস্ব প্রপার্টি হাঁটা আর __proto__, constructor ও prototype এড়িয়ে যাওয়া। বিস্তারিত পারফরম্যান্স ও নিরাপত্তা FAQ-এ।

৫. দ্রুত রেফারেন্স

সিনট্যাক্স বৈধ নোট
{"a": 1,} না ট্রেইলিং কমা
{'a': 1} না সিঙ্গেল কোট
{a: 1} না কোট ছাড়া কি
{"a": 1} // comment না কমেন্ট
"hello" হ্যাঁ টপ-লেভেল স্ট্রিং বৈধ, কিন্তু API-তে কম ব্যবহৃত
{"a": 1e3} হ্যাঁ সূচক সমর্থিত
{"a": 01} না শুরুর শূন্য
{"a": NaN} না অবৈধ লিটেরাল; stringify একে null করে
\uFEFF{"a":1} না BOM JSON টেক্সটের অংশ নয়
{"a":1,"a":2} বিতর্কিত বাস্তবায়ন শেষ মান নেয়, কিন্তু নির্ভর করবেন না