RFC 8259 থেকে JSON ব্যাকরণের সীমানা পড়া
সংক্ষেপে: RFC 8259-এর ব্যাকরণ প্রোডাকশনগুলো সহজ ভাষায় অনুবাদ: কোন অক্ষর সরাসরি থাকতে পারে, কোনটি এস্কেপ করতে হবে, আর স্পেসিফিকেশন জেনে কোথায় ছাড় দিয়েছে।

মূল কথা: JSON সিনট্যাক্স এরর এলে পুরো ডকুমেন্ট আবার না পড়ে প্রথম অবৈধ অক্ষরটি খুঁজে সেটিকে সিঙ্গেল কোট, ট্রেইলিং কমা, কমেন্ট বা কন্ট্রোল ক্যারেক্টার হিসেবে শ্রেণিবদ্ধ করুন; এনকোডিংয়ের দিকে BOM ছাড়া UTF-8 নিশ্চিত করুন এবং 2^53 - 1-এর বেশি ইন্টিজার স্ট্রিং হিসেবে বহন করুন।
স্পেসিফিকেশনের মূল অংশ মাত্র ডজনখানেক পৃষ্ঠা, তবু প্রতিটি "MUST", "SHOULD" ও "MAY"-এর পেছনে লুকিয়ে আছে এমন এক ঘটনা, যা একসময় একটা গোটা দলকে সারারাত জাগিয়ে রেখেছিল।
১. তিনটি পাঠ্যের সম্পর্ক
JSON শিখতে গিয়ে প্রায়ই বিভ্রান্তি হয় তিনটি সংখ্যার কারণে: RFC 4627, RFC 7159, RFC 8259, সঙ্গে ECMA-404। সম্পর্কটা আসলে বেশ পরিষ্কার:
| পাঠ্য | তারিখ | অবস্থা | মূল পার্থক্য |
|---|---|---|---|
| RFC 4627 | ২০০৬ | বাতিল | টপ লেভেল অবশ্যই অবজেক্ট বা অ্যারে |
| RFC 7159 | ২০১৪ | বাতিল | টপ লেভেল যেকোনো JSON ভ্যালুতে শিথিল |
| RFC 8259 | ২০১৭.১২ | চলমান স্ট্যান্ডার্ড | স্পষ্ট UTF-8, আন্তঃকার্যক্ষমতার নির্দেশনা যোগ |
| ECMA-404 | ২০১৩, দ্বিতীয় সংস্করণ ২০১৭.১২ | ব্যাকরণ স্পেসিফিকেশন | কেবল ব্যাকরণ নির্ধারণ করে, এনকোডিং বা পরিবহন কভার করে না |
কোনটি পড়বেন? RFC 8259 একটিই যথেষ্ট। ব্যাকরণের অংশে ECMA-404 সমতুল্য; পার্থক্য হলো RFC 8259 আরও বলে "UTF-8 হিসেবে পরিবহন" এবং "ইমপ্লিমেন্টারদের যে আন্তঃকার্যক্ষমতা সমস্যাগুলো খেয়াল রাখা উচিত"।
২. ছয়টি ভ্যালু টাইপ
স্পেসিফিকেশনের ব্যাকরণ প্রোডাকশনগুলো একটি মাত্র ডায়াগ্রামে সংক্ষিপ্ত করা যায়:
JSON-text = ws value ws
value = object / array / number / string / "true" / "false" / "null"
object = "{" [ member *( "," member ) ] "}"
member = string ws ":" ws value
array = "[" [ value *( "," value ) ] "]"
মূল বিষয় মাত্র ছয়টি:
- অবজেক্ট কার্লি ব্রেস ব্যবহার করে, সদস্য কমা দিয়ে আলাদা, আর শেষ সদস্যের পরে কমা অনুমোদিত নয়;
- অ্যারে স্কয়ার ব্র্যাকেট ব্যবহার করে, উপাদান কমা দিয়ে আলাদা, আর ট্রেইলিং কমাও তেমনি অনুমোদিত নয়;
- সংখ্যা-তে শুরুর
+নেই, শুরুর শূন্য নেই (01অবৈধ), খালি দশমিক বিন্দু নেই (1.অবৈধ), আরNaNবাInfinityনেই; - স্ট্রিং অবশ্যই ডাবল কোট ব্যবহার করবে; সিঙ্গেল কোট অবৈধ;
- বুলিয়ান ও null ছোট হাতের লিটেরাল;
TrueওNULLদুটোই অবৈধ; - কাঠামোগত উপাদানের মধ্যে কেবল হোয়াইটস্পেস
0x20(স্পেস),0x09(ট্যাব),0x0A(লাইন ফিড) ও0x0D(ক্যারেজ রিটার্ন) থাকতে পারে।
নিচের টুকরোটি বৈধ JSON, যদিও প্রথম দেখে অনেকে ভুল ভাবেন — কারণ এটি টপ লেভেলে একটি খালি স্ট্রিং:
"just a string"
RFC 7159 থেকে টপ লেভেল সত্যিই যেকোনো ভ্যালু হতে পারে। তবে API-তে এখনো কেবল অবজেক্ট বা অ্যারে ব্যবহারের সুপারিশ করা হয়; কারণটি Section 6-এ আছে।
৩. স্ট্রিংয়ের ভেতরে আসলে সরাসরি কী থাকতে পারে
এটাই সেই অংশ যেখানে "আমার মেশিনে ঠিক চলছে" দিয়ে সবচেয়ে সহজে ধোঁকা খাওয়া যায়। স্ট্রিংয়ের জন্য স্পেসিফিকেশনের নিয়ম:
- সরাসরি থাকার অনুমতি আছে:
%x20-21,%x23-5B,%x5D-10FFFF; - অর্থাৎ ডাবল কোট
"(0x22) ও ব্যাকস্ল্যাশ\(0x5C) অবশ্যই এস্কেপ করতে হবে; - 0x20-এর নিচের সব কন্ট্রোল ক্যারেক্টার এস্কেপ করতে হবে, লাইন ফিড, ট্যাব ও ক্যারেজ রিটার্নসহ;
- উপলব্ধ এস্কেপ সিকোয়েন্স কেবল
\",\\,\/,\b,\f,\n,\r,\tও\uXXXX।
{
"ok": "Chinese text, emoji 🎉 and full-width symbols can be written directly",
"escaped": "A line break goes here\nand a tab\tfinishes it",
"control": "\u0007 is the bell character and must be escaped",
"quote": "He said: \"don't use single quotes\""
}
\/ একটি লেগাসি এস্কেপ (এটি </script>-কে আগে বন্ধ হওয়া থেকে বাঁচায়)। আধুনিক পরিস্থিতিতে খুব কম দরকার, তবে পার্সারকে এটি সমর্থন করতে হয়।
৪. এনকোডিং: UTF-8, BOM নেই
RFC 8259-এর Section 8.1 তিনটি কথা বলে:
- আন্তঃবিনিময়ের জন্য ব্যবহৃত JSON টেক্সট অবশ্যই UTF-8-এ এনকোড করতে হবে;
- ইমপ্লিমেন্টেশন টেক্সটের শুরুতে BOM (
EF BB BF) যোগ করতে পারবে না; - পার্সার আরও মজবুত হতে BOM উপেক্ষা করতে পারে।
বাস্তব জগতে দুইটি সাধারণ ব্যর্থতা:
- Windows নোটপ্যাড "UTF-8 with BOM" হিসেবে সেভ করে, ফাইলটি
JSON.parse-এUnexpected tokenদিয়ে প্রত্যাখ্যাত হয়; - GBK হিসেবে সেভ করা চীনা JSON ফাইল কেবল UTF-8 ডিকোড করা পাইপলাইনে এলোমেলো হয়ে যায়, অথচ সিনট্যাক্স হঠাৎ পাসও করে, ফলে এরর ব্যবসায়িক স্তরে গিয়ে দেখা দেয়।
হ্যান্ডলিং সহজ: ফাইল পড়ার সময় BOM সরান, তারপর স্পষ্টভাবে UTF-8 ডিকোড করুন; লেখার সময় নিশ্চিত করুন BOM যোগ হচ্ছে না।
/**
* স্ট্রিংয়ের শুরুতে থাকা UTF-8 BOM সরিয়ে দেয়।
* @param text মূল টেক্সট
* @returns BOM ছাড়া টেক্সট
*/
function stripBom(text: string): string {
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
}
প্রসঙ্গে মিডিয়া টাইপ নিয়ে একটি কথা: application/json আর charset প্যারামিটার গ্রহণ করে না; লিখলে এরর হয় না, তবে এটি পুরোনো প্রথা।
৫. স্পেসিফিকেশন যেখানে জেনে ছাড় দিয়েছে
স্পেসিফিকেশন যেসব জায়গায় কিছু নির্দিষ্ট করতে রাজি হয়নি, ঠিক সেখান থেকেই ইমপ্লিমেন্টেশনের পার্থক্য আসে।
১. সংখ্যার রেঞ্জ ও নির্ভুলতা
RFC 8259 কেবল বলে "এই স্পেসিফিকেশন সংখ্যার রেঞ্জ বা নির্ভুলতা সীমিত করে না", সঙ্গে উল্লেখ করে বেশিরভাগ ইমপ্লিমেন্টেশন IEEE 754 ডাবল প্রিসিশন ব্যবহার করে।
এর মানে একই 9223372036854775807 JavaScript-এ 9223372036854776000 হয়, অথচ Java-র long-এ নিখুঁত থাকে।
সিদ্ধান্ত: 2^53 - 1-এর বেশি ইন্টিজার এবং বহু দশমিক অঙ্কের নির্ভুলতা দরকার এমন টাকার পরিমাণ সবসময় স্ট্রিং হিসেবে বহন করুন, স্কিমায় pattern স্পষ্টভাবে লিখুন।
২. ডুপ্লিকেট কি
স্পেসিফিকেশনের ভাষা "অবজেক্টের ভেতরের নামগুলো উচিত (SHOULD) অনন্য হওয়া"; না হলে কী করবে তা বলে না।
JSON.parseশেষটি রাখে;- কিছু পার্সার প্রথমটি রাখে;
- কিছু লাইব্রেরি সরাসরি এরর দেয়।
সিদ্ধান্ত: ডুপ্লিকেট কি লিখবেন না, আর আপনার ভ্যালিডেটরে কোনো নির্দিষ্ট আচরণের উপর নির্ভর করবেন না। বিল্ডের সময় "ডুপ্লিকেট কি সনাক্তকরণ" ধাপ যোগ করা পরে তদন্ত করার চেয়ে অনেক সস্তা।
৩. গভীরতা ও দৈর্ঘ্যের সীমা
স্পেসিফিকেশন নেস্টিং গভীরতার কোনো সীমা দেয় না, কিন্তু প্রতিটি ইমপ্লিমেন্টেশনে আছে। V8 কয়েক হাজার স্তরের নেস্টিংয়ে RangeError: Maximum call stack size exceeded throws করে।
সার্ভারকে নিজের সীমা আরোপ করতে হবে; এটি নিরাপত্তার দাবি, স্পেসিফিকেশনের দাবি নয়।
৬. পাল্টা-উদাহরণ দিয়ে বোঝা যাচাই
নিচের প্রতিটি লাইন অবৈধ JSON। পার্সারে দিন এবং দেখুন এরর মেসেজগুলো সহায়ক কি না:
{ 'name': 'json' } // single quotes
{ name: "json" } // key name not quoted
{"a": 1,} // trailing comma
[1, 2, 3,] // trailing comma in an array
{"a": 01} // leading zero
{"a": +1} // leading plus sign
{"a": .5} // missing integer part
{"a": NaN} // non-standard literal
{"a": undefined} // not a JSON value
{"a": 1} // comment // comment
একটি মজার সীমানা কেস: {"a": 1}-এর পরে কমেন্ট JSON5-এ বৈধ, JSONC-তে বৈধ, আর স্ট্যান্ডার্ড JSON-এ অবৈধ।
তাই "কনফিগারেশন ফাইল ঠিক চলছে কিন্তু API প্রত্যাখ্যান করছে" প্রায় সবসময় ডায়ালেক্টের সমস্যা।
৭. প্রোডাকশন চেকলিস্ট
- [ ] বাইরে পাঠানো সব JSON
charsetছাড়াapplication/jsonব্যবহার করে; - [ ] ফাইল পড়া/লেখার পাইপলাইন BOM সরায় এবং স্পষ্ট UTF-8 ব্যবহার করে;
- [ ]
2^53-এর বেশি ইন্টিজার স্ট্রিং হিসেবে বহন করা হয় এবং স্কিমায় টীকা দেওয়া আছে; - [ ] বিল্ডের সময় ডুপ্লিকেট কি সনাক্ত করা হয়;
- [ ] সার্ভার রিকোয়েস্ট বডির আকার ও নেস্টিং গভীরতা সীমিত করে;
- [ ] পাবলিক API-র টপ-লেভেল ভ্যালু অবজেক্ট বা অ্যারে, কখনো খালি স্কেলার নয়।
সাধারণ প্রশ্ন
একটি টেক্সট বৈধ JSON কি না দ্রুত কীভাবে বুঝব?
টেক্সটটি পার্সার বা অনলাইন পার্সিং টুলে দিন, প্রথম অবৈধ অক্ষরের অবস্থান পড়ুন, তারপর RFC 8259-এর ছয় ভ্যালু টাইপ ও হোয়াইটস্পেস নিয়মের সঙ্গে মিলিয়ে দেখুন। টপ লেভেল যেকোনো JSON ভ্যালু হতে পারে, তবে কাঠামোগত উপাদানের মধ্যে কেবল স্পেস, ট্যাব, লাইন ফিড ও ক্যারেজ রিটার্ন অনুমোদিত। একটি সিঙ্গেল কোট, আনকোয়োটেড কি, ট্রেইলিং কমা বা কমেন্ট থাকলে তা স্ট্যান্ডার্ড JSON নয়।
JSON-কে কেন UTF-8 ব্যবহার করতে হয় এবং BOM ছাড়া রাখতে হয়?
RFC 8259-এর Section 8.1 আন্তঃবিনিময়ের JSON টেক্সট UTF-8-এ এনকোড করার দাবি করে, ইমপ্লিমেন্টেশন শুরুতে BOM যোগ করতে পারবে না বলে, এবং মজবুতিের জন্য পার্সারকে BOM উপেক্ষা করার অনুমতি দেয়। BOM-যুক্ত ফাইল JSON.parse-এ Unexpected token দিয়ে প্রত্যাখ্যাত হয়, যদিও চোখে কিছু ভুল মনে হয় না। তাই পড়ার সময় BOM সরান এবং স্পষ্টভাবে UTF-8 ডিকোড করুন।
এই ব্যাকরণের সীমা এড়াতে JSON5 বা JSONC ব্যবহার করব?
হ্যাঁ, তবে কেবল কনফিগারেশন ফাইলের মতো স্থানীয় পাঠের জন্য; সিস্টেমের মধ্যে বিনিময় করা ডেটা এখনো স্ট্যান্ডার্ড JSON-ই হওয়া উচিত। পাল্টা-উদাহরণ দেখায় {"a": 1}-এর পরে কমেন্ট JSON5 ও JSONC-তে বৈধ কিন্তু স্ট্যান্ডার্ড JSON-এ অবৈধ, তাই "কনফিগ ফাইল চলছে কিন্তু API প্রত্যাখ্যান করছে" প্রায় সবসময় ডায়ালেক্ট সমস্যা। রূপান্তরের সময় আগে কমেন্ট, সিঙ্গেল কোট ও ট্রেইলিং কমা সরিয়ে ফলাফল স্ট্যান্ডার্ড পার্সারকে দিন।