सिंटैक्स और फ़ॉर्मैट FAQ: कमेंट, ट्रेलिंग कॉमा और प्रिसिज़न
मुख्य बात: क्या कमेंट की अनुमति है, ट्रेलिंग कॉमा एरर क्यों देता है, क्या कुंजियों पर कोट ज़रूरी हैं, 9007199254740993 क्यों बदल जाता है — बारह निष्कर्ष, हर एक RFC 8259 के सामने जाँचा गया।

मुख्य बात: प्रक्रिया की सीमा पार करने वाली हर चीज़ के लिए सादा RFC 8259 JSON लिखें — डबल कोट्स, कोई कमेंट नहीं, कोई ट्रेलिंग कॉमा नहीं, BOM के बिना UTF-8; कमेंट चाहिए तो कॉन्फ़िग के लिए JSONC या JSON5 उपयोग करें और रनटाइम पर वापस बदलें; और 2^53-1 से ऊपर के पूर्णांक सर्वर पर स्ट्रिंग के रूप में सीरियलाइज़ करें।
इन प्रश्नों में एक बात साझा है: ये देखने में लगते हैं कि "काम करने चाहिए", लेकिन स्पेसिफिकेशन वास्तव में बहुत सख्त है।
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 के व्याकरण में कमेंट के लिए कोई production नहीं है, इसलिए कोई भी // या /* */ पार्सिंग विफल कर देता है।
अगर आप कॉन्फ़िग में स्पष्टीकरण रखना चाहते हैं, तो तीन विकल्प हैं:
{
"_comment": "以下超时单位是毫秒",
"timeout": 3000,
"retry": {
"$doc": "重试次数,0 表示不重试",
"count": 3
}
}
_commentजैसे पारंपरिक फ़ील्ड उपयोग करें जिन्हें पढ़ने वाला बस अनदेखा कर दे;$-उपसर्ग वाले फ़ील्ड उपयोग करें ताकि व्यावसायिक फ़ील्ड से टकराव की संभावना कम हो;- कॉन्फ़िग के लिए सीधे JSONC या YAML पर स्विच करें और रनटाइम पर मानक JSON में बदलें।
ट्रेलिंग कॉमा एरर क्यों देता है?
क्योंकि व्याकरण में array और object दोनों "तत्व + विभाजक" संरचनाएँ हैं, और अंतिम तत्व के बाद कॉमा की अनुमति नहीं है।
{
"a": 1,
"b": 2,
}
ऊपर के स्निपेट में } से पहले एक अतिरिक्त कॉमा है, और पार्सर के कार्यान्वयन के अनुसार एरर आमतौर पर कॉमा पर या क्लोज़िंग ब्रेस पर बताई जाती है।
JavaScript से माइग्रेट करते समय यह सबसे आसान जाल भी है — JS ऑब्जेक्ट लिटरल ट्रेलिंग कॉमा की अनुमति देते हैं, JSON नहीं।
क्या सिंगल कोट्स की अनुमति है?
नहीं। स्ट्रिंग में डबल कोट्स ही उपयोग होने चाहिए; 'text' एक सिंटैक्स एरर है।
अगर डेटा Python के repr(), किसी शेल स्क्रिप्ट, या हाथ से लिखे कॉन्फ़िग से कॉपी किया गया है, तो पार्सर के उदार होने की उम्मीद करने के बजाय
पहले किसी टूल से उसे बदलें।
क्या कुंजियाँ बिना कोट के हो सकती हैं?
नहीं। ऑब्जेक्ट सदस्य नाम ऐसी स्ट्रिंग होने चाहिए जो डबल कोट्स में लिपटी हो।
{ name: "json" }
यह JavaScript ऑब्जेक्ट लिटरल है, JSON नहीं। कोट जोड़ने पर यह मान्य हो जाता है:
{ "name": "json" }
क्या शीर्ष स्तर array या string हो सकता है?
हाँ। RFC 8259 शीर्ष स्तर पर किसी भी JSON मान की अनुमति देता है: ऑब्जेक्ट, array, string, संख्या, true, false और null सभी मान्य हैं।
इंजीनियरिंग प्रैक्टिस में, हालाँकि, शीर्ष स्तर पर केवल ऑब्जेक्ट या array उपयोग करने की सलाह दी जाती है, दो कारणों से:
- संगतता: पुराने पार्सर (RFC 4627 युग के) शीर्ष स्तर पर ऑब्जेक्ट या array की अपेक्षा करते हैं;
- सुरक्षा: शीर्ष-स्तरीय array के साथ ऐतिहासिक रूप से JSON hijacking का जोखिम रहा है। आधुनिक ब्राउज़र इसे ठीक कर चुके हैं, लेकिन पुराने सिस्टम अब भी इसी परंपरा को मानते हैं।
इसलिए "hello" मान्य JSON है और ["a", "b"] भी, लेकिन {"data": [...]} लौटाने वाला API अधिक सुरक्षित है।
2. एन्कोडिंग और स्ट्रिंग
क्या फ़ाइल के शुरू का BOM पार्सिंग तोड़ता है?
हाँ। RFC 8259 स्पष्ट रूप से कहता है कि JSON टेक्स्ट UTF-8 में प्रसारित होना चाहिए और उसमें byte order mark (BOM) नहीं जोड़ा जाना चाहिए।
BOM फ़ाइल के ठीक शुरू में तीन बाइट्स EF BB BF होता है। यह पूरी तरह अदृश्य है, लेकिन स्ट्रिंग की पहली स्थिति घेर लेता है,
इसलिए पार्सर निष्कर्ष निकालता है कि "पहला अक्षर { नहीं है" और एरर देता है — और बताई गई लाइन व कॉलम आमतौर पर लाइन 1, कॉलम 1 होती है, जो उलझाने वाली लगती है।
Windows Notepad का "Save as UTF-8" डिफ़ॉल्ट रूप से BOM शामिल करता है। इसे जाँचने और हटाने के लिए:
# जाँचें: efbbbf से शुरू होने वाला आउटपुट मतलब BOM मौजूद है
head -c 3 data.json | xxd
# हटाएँ: tail से पहले 3 बाइट्स छोड़ें
tail -c +4 data.json > clean.json
आधुनिक JavaScript का JSON.parse BOM सहन कर लेता है (V8 इसे अंदर ही छोड़ देता है), लेकिन Java, Go और Python के सख्त कार्यान्वयन नहीं करते,
जो "एक ही फ़ाइल अलग-अलग भाषाओं में अलग परिणाम देती है" का एक सामान्य कारण है।
क्या चीनी अक्षरों को \uXXXX में बदलना ज़रूरी है?
नहीं। RFC 8259 के अनुसार JSON टेक्स्ट UTF-8 होना चाहिए, इसलिए चीनी सीधे आ सकता है; {"name": "张三"} पूरी तरह मान्य है।
\uXXXX केवल एक समतुल्य वैकल्पिक वर्तनी है, जिसका उपयोग उन चैनलों के लिए होता है जो अक्षर को सीधे निरूपित नहीं कर सकते। अनुभव नियम:
- HTTP request body, फ़ाइलें, डेटाबेस: चीनी सीधे लिखें, प्रति अक्षर 3 बाइट्स बचें;
- HTTP हेडर, URL पैरामीटर, किसी अन्य स्ट्रिंग के अंदर एम्बेड करना: एन्कोडिंग समस्याओं से बचने के लिए escapes उपयोग करें।
केवल वे अक्षर जिन्हें अनिवार्य रूप से escape करना होता है, control characters (U+0000 से U+001F) हैं। उदाहरण के लिए, newline को \n के रूप में लिखना चाहिए;
आप स्ट्रिंग के अंदर शाब्दिक newline नहीं डाल सकते।
क्या emoji सीधे स्ट्रिंग में जा सकते हैं?
हाँ, लेकिन याद रखें कि यह दो UTF-16 code units लेते हैं।
Emoji और कई दुर्लभ अक्षर (जैसे 𠮷) 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 से गणना की जाए, तो एक emoji दो स्लॉट लेता है; अगर बैकएंड बाइट्स गिनता है, तो चार।
परिभाषाएँ मिलाते समय स्पष्ट कहें कि आपका मतलब code points, code units, या bytes है।
3. संख्याएँ और विशेष मान
बड़ा पूर्णांक 9007199254740993 बदलकर 9007199254740992 क्यों हो गया?
क्योंकि JavaScript का JSON.parse संख्याओं को IEEE 754 डबल-प्रिसिज़न फ़्लोट में बदल देता है, और सबसे बड़ा सुरक्षित पूर्णांक 2^53-1 है।
Number.MAX_SAFE_INTEGER === 9007199254740991 // true
9007199254740993 // actually yields 9007199254740992
2^53-1 से ऊपर कोई भी संख्या अपने अंतिम अंकों में राउंड हो सकती है। सामान्य शिकार स्नोफ़्लेक एल्गोरिदम से बने 19-अंकों वाले यूज़र ID, ऑर्डर नंबर, और नैनोसेकंड टाइमस्टैम्प हैं।
तीन उपाय, प्राथमिकता के क्रम में:
- सर्वर पर ID को स्ट्रिंग के रूप में सीरियलाइज़ करें:
"id": "9007199254740993", जिससे प्रिसिज़न समस्या जड़ से हट जाती है; - लॉसलेस पार्सिंग लाइब्रेरी उपयोग करें:
JSON.parseके विकल्प बड़े पूर्णांक ज्यों-के-त्यों रख सकते हैं, उदाहरण के लिएjson-bigint; - केवल प्रदर्शन के लिए रेगुलर एक्सप्रेशन से निकालें: पूरे दस्तावेज़ को पार्स न करें, लक्ष्य फ़ील्ड को टेक्स्ट के रूप में मिलाएँ।
ध्यान दें कि JSON.stringify में भी सममित समस्या है: सुरक्षित पूर्णांक सीमा से बाहर की संख्या सीरियलाइज़ेशन के बाद अपना मूल मान नहीं रख सकती।
क्या NaN और Infinity JSON में जा सकते हैं?
नहीं। JSON का number production केवल दशमलव संकेतन की अनुमति देता है, और NaN, Infinity और -Infinity मान्य JSON मान नहीं हैं।
JavaScript का तरीका "चुपचाप गिरावट" है:
JSON.stringify({ value: NaN }); // '{"value":null}'
JSON.stringify({ value: Infinity }); // '{"value":null}'
कोई एरर नहीं, NaN बस null बन जाता है। यही वह क्लासिक दृश्य है जहाँ "फ़्रंटएंड ने NaN भेजा और बैकएंड को null मिला"।
जब आपको ऐसा अर्थ व्यक्त करना हो, तो स्ट्रिंग या तय किए गए sentinel मानों से स्पष्ट रूप से व्यक्त करें:
{ "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देता है, चिह्न खो जाता है।
4. ऑब्जेक्ट अर्थ
डुप्लिकेट कुंजियों पर क्या होता है?
व्यवहार कार्यान्वयन पर निर्भर है; RFC 8259 केवल यह कहता है कि कुंजी नाम "चाहिए" अद्वितीय हों और डुप्लिकेट पर क्या होगा यह निर्दिष्ट नहीं करता।
व्यवहार में अधिकांश कार्यान्वयन अंतिम मान लेते हैं:
JSON.parse('{"a":1,"a":2}') // { a: 2 }
लेकिन आप इस पर भरोसा नहीं कर सकते। कुछ Java लाइब्रेरी, Go का encoding/json, और Python का json संस्करणों में भिन्न होते हैं:
कुछ पहला मान लेते हैं, कुछ एरर देते हैं, और कुछ अंतिम लेते हैं। डुप्लिकेट कुंजियों को एरर मानना भाषाओं के पार सबसे सुरक्षित तरीका है।
आप डुप्लिकेट कुंजियाँ आँख से नहीं ढूँढ सकते — minified JSON में एक ही कुंजी हज़ारों अक्षरों के अंतर पर हो सकती है। स्क्रिप्ट उपयोग करें:
# उसी स्तर पर एक से अधिक बार आने वाली कुंजियाँ निकालें, मोटे जाँच के रूप में
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 ऑब्जेक्ट के अंदर "फ़ील्ड गायब हो जाता है" और array के अंदर "null बन जाता है" का अर्थ देता है।
अगर बैकएंड "फ़ील्ड गायब" और "फ़ील्ड null है" के लिए अलग-अलग शाखाएँ लेता है, तो यहाँ आपको असंगति मिलती है।
जब आपको "कोई मान नहीं" स्पष्ट रूप से व्यक्त करना हो, तो undefined के बजाय null उपयोग करें।
{"__proto__": {...}} पार्स करने के बाद क्या होता है?
JSON.parse खुद सुरक्षित है: __proto__ को सामान्य own property माना जाता है और prototype नहीं बदलता।
खतरनाक बात पार्सिंग के बाद का merge ऑपरेशन है:
const patch = JSON.parse('{"__proto__": {"isAdmin": true}}');
Object.assign({}, patch); // some implementations pollute the prototype
सही तरीका है own properties पर इटरेट करना और __proto__, constructor और prototype छोड़ देना।
विवरण परफ़ॉर्मेंस और सुरक्षा FAQ में हैं।
5. त्वरित संदर्भ
| सिंटैक्स | मान्य | टिप्पणियाँ |
|---|---|---|
{"a": 1,} |
नहीं | ट्रेलिंग कॉमा |
{'a': 1} |
नहीं | सिंगल कोट्स |
{a: 1} |
नहीं | बिना कोट की कुंजी |
{"a": 1} // comment |
नहीं | कमेंट |
"hello" |
हाँ | शीर्ष-स्तरीय string मान्य है, लेकिन API में शायद ही उपयोग होता है |
{"a": 1e3} |
हाँ | घातांक समर्थित हैं |
{"a": 01} |
नहीं | शुरुआती शून्य |
{"a": NaN} |
नहीं | अमान्य लिटरल; stringify इसे null बना देता है |
\uFEFF{"a":1} |
नहीं | BOM JSON टेक्स्ट का हिस्सा नहीं है |
{"a":1,"a":2} |
विवादित | कार्यान्वयन अंतिम मान लेते हैं, लेकिन इस पर निर्भर न रहें |