एक ही JSON, चार टूल, चार परिणाम: फ़ॉर्मैटर कैसे चुनें
मुख्य बात: फ़ॉर्मैटिंग नुकसान-मुक्त नहीं है। अग्रणी शून्य, BOM या 20-अंकीय पूर्णांक वाला एक ही JSON Python, Node, jq और json.tool में अलग परिणाम देता है: कुछ त्रुटि देते हैं, कुछ चुपचाप मान बदल देते हैं। चार रास्तों पर स्वयं चलाए गए दस नमूनों से चयन तालिका और एक सख़्त जाँच स्क्रिप्ट मिलती है।

निष्कर्ष: JSON फ़ॉर्मैटरों के बीच सबसे घातक अंतर इंडेंट की जगहों की संख्या नहीं है, बल्कि यह है कि एक ही इनपुट पर कोई टूल त्रुटि देता है और कोई चुपचाप डेटा बदल देता है। टूल चुनने से पहले उसमें सीमांत मान वाला एक नमूना चला लें — यह फ़ीचर सूची से कहीं ज़्यादा बताता है।
लोग सोचते हैं कि फ़ॉर्मैटिंग मतलब «टेक्स्ट को दोबारा सजाना» है। हकीकत में हर रास्ता अपने पार्सर से उस टेक्स्ट की नई व्याख्या करता है। पार्सर ढीला हो, तो जो त्रुटि फेंकी जानी थी वह मूल्य में चुपचाप बदलाव बन जाती है — और पृष्ठ पर हरे रंग का «सफलतापूर्वक फ़ॉर्मैट किया गया» भरोसा दिलाता रहता है।
1. लक्षण: एक इनपुट, चार रास्ते, चार परिणाम
परीक्षण वातावरण Python 3.11.1, Node v22.13.1, jq 1.7 है। हाथ से बनाए दस छोटे JSON स्ट्रिंग standard input से डाले गए, डिस्क पर नहीं लिखे गए, कॉन्फ़िग नहीं बदला। चार रास्ते: json.loads, JSON.parse, jq -c ., python -m json.tool। ERR का मतलब exception फेंककर रुकना, SILENT का मतलब कोई त्रुटि नहीं पर आउटपुट इनपुट से अलग हो चुका है।
| नमूना | python json.loads | node JSON.parse | jq -c . | python -m json.tool |
|---|---|---|---|---|
S1 {"a":1,"b":[1,2]} |
OK | OK | OK | OK |
S2 {"a":1,} |
ERR | ERR | ERR | ERR |
S3 {'a':1} |
ERR | ERR | ERR | ERR |
S4 {"a":01} |
ERR char 6 | ERR position 6 | SILENT {"a":1} |
ERR |
S5 {"a":1 /*x*/} |
ERR char 7 | ERR | ERR | ERR |
S6 \ufeff{"a":1} |
ERR utf-8-sig | ERR position 0 | SILENT {"a":1} |
ERR |
S7 {"id":9007199254740993} |
OK exact | SILENT 9007199254740992 | OK exact | OK |
S8 {"a":1,"a":2} |
SILENT {"a":2} |
SILENT {"a":2} |
SILENT {"a":2} |
OK |
S9 {"a":1 |
ERR char 6 | ERR | ERR EOF | ERR |
S10 <html>502</html> |
ERR char 0 | ERR Unexpected token '<' | ERR | ERR |
दस में चार नमूनों ने «बिना त्रुटि पर डेटा बदल गया» दिया: S4, S6, S7, S8। यानी सिर्फ हरी बत्ती «फ़ॉर्मैट सफल» पर भरोसा करें, तो चालीस प्रतिशत संभावना है कि आपके हाथ में मूल डेटा नहीं बचा। चालाकी यह है कि इनमें से कोई भी सिंटैक्स-त्रुटि सूची में नहीं है — फ़ाइल खुलती है, कॉपी होती है, एक्सपोर्ट होती है, गलती सामग्री में है, सिंटैक्स में नहीं।
2. कारण: सिंटैक्स परत और अर्थ परत दो अलग चीज़ें हैं
RFC 8259 साफ कहता है: धारा 1.2 अधिकतम अंतर-संचालन के लिए ECMA-404 की कुछ अनुमत प्रथाओं से बचने की सिफ़ारिश करती है; धारा 4 ऑब्जेक्ट नामों को केवल SHOULD के रूप में अद्वितीय मांगती है, डुप्लिकेट कुंजियों के व्यवहार को तय किए बिना; धारा 6 संख्याओं पर सिर्फ अंतर-संचालन सिफ़ारिशें देती है। ECMA-404 एक कदम आगे जाकर स्पष्ट लिखता है कि वह «संख्याओं के अर्थ के प्रति उदासीन» है — केवल अंक-श्रृंखला के लिखने का नियम देता है, भाषा में भंडारण का नहीं। यानी विनिर्देश एक बड़ा सीमांत क्षेत्र implementer पर छोड़ देता है।
इसलिए «फ़ॉर्मैटिंग» दो परतों में बँट जाती है: सिंटैक्स परत तय करती है कि टेक्स्ट वैध है या नहीं, अर्थ परत तय करती है कि वह टेक्स्ट किस मूल्य में बदलेगा। पहले का मानक उत्तर है, दूसरे का नहीं। jq का 01 को 1 पढ़ना और शुरुआती BOM छोड़ना अर्थ परत का चुनाव है; Node का 20-अंकीय पूर्णांक को IEEE 754 double में ठूँसना runtime का चुनाव है; चारों रास्तों का {"a":1,"a":2} पर आख़िरी मूल्य रखना बड़े पार्सरों की साझा समझ है। कोई «पार्स हो सकता है» का उल्लंघन नहीं करता, पर सब डेटा बदल देते हैं।
JSON parse त्रुटियाँ कैसे ठीक करें सिंटैक्स परत पर काम करता है, त्रुटि के अक्षरों को निर्देशांक में बदलता है। यह लेख उससे ठीक पहले वाले चरण को संभालता है — अगर टूल ने त्रुटि दी ही नहीं, तो आपको निर्देशांक भी नहीं मिलते।
3. डेटा के चार मौन बदलाव, एक-एक करके
चारों मौन नमूनों को अलग निकालें, तो बदलाव इतने छोटे हैं कि सरसरी नज़र में पकड़ में नहीं आते:
{"a": 01} // leading zero: jq emits {"a":1}
\ufeff{"a": 1} // BOM: jq skips the first three bytes
{"id": 9007199254740993} // 20-digit int: JSON.parse gives ...992
{"a": 1, "a": 2} // duplicate key: all four keep {"a":2}
अग्रणी शून्य और BOM उस प्रकार के हैं «इनपुट पहले से अवैध था, पर टूल ने नरमी दिखाई»। 01 मानक JSON में अवैध है (RFC 8259 धारा 6), BOM भी JSON टेक्स्ट के शुरू में नहीं होना चाहिए; Python और Node दोनों स्पष्ट त्रुटि देते हैं, jq आगे बढ़ने को चुनता है। यह नरमी लॉग सफ़ाई में सुविधाजनक है, अनुबंध जाँच में घातक: अपस्ट्रीम सोचता है कि उसने गलत डेटा भेजा जिसे सुधारना है, जबकि डाउनस्ट्रीम पहले से «सामान्य दिखने वाला» परिणाम थामे है।
बड़ा पूर्णांक उस प्रकार का है «इनपुट पूरी तरह वैध है, पर runtime में समाता नहीं»। 9007199254740993 वैध JSON संख्या है, पर 2^53-1 से ऊपर है; JSON.parse सिर्फ 9007199254740992 दिखा सकता है। Python का int और jq मूल मान बचा लेते हैं। अगर इस JSON में ऑर्डर नंबर या Snowflake ID है, तो आख़िरी अंक बदलने का मतलब है हिसाब मेल न खाना।
डुप्लिकेट कुंजियाँ उस प्रकार की हैं «विनिर्देश चुप है, implementation खुद चुनती है»। चारों रास्ते बिना किसी चेतावनी के चुपचाप अंतिम मान ले लेते हैं। RFC 8259 सिर्फ SHOULD कहता है, इसलिए यह बग नहीं है — पर कॉन्फ़िग जोड़ने या पैच मर्ज करते समय यह सबसे मुश्किल से मिलने वाली समस्या है।
4. त्रुटि ढूँढ़ने की क्षमता: कौन पंक्ति-स्तंभ देता है, कौन सिर्फ एक पंक्ति
छह असफल नमूनों पर भी चार रास्तों की जानकारी बहुत अलग है। Python का JSONDecodeError एक साथ msg, lineno, colno, pos देता है; Node v22 position N (line 1 column M) के साथ गलती वाली जगह का मूल अंश जोड़ता है; python -m json.tool Python की जानकारी दोहराता है; jq सिर्फ parse error: ... at line N एक पंक्ति देता है, बिना वर्ण-ऑफ़सेट के।
एक-पंक्ति के minified JSON पर पंक्ति और स्तंभ लगभग बेकार हैं — सब कुछ पंक्ति 1 में है। ज़रूरत वर्ण-ऑफ़सेट की है। नीचे की स्क्रिप्ट जाँच को «त्रुटि + ऑफ़सेट + संदर्भ» में समेट देती है:
import json, sys
def strict_check(text: str) -> None:
try:
json.loads(text)
print("OK")
except json.JSONDecodeError as e:
print(f"ERR {e.msg} | char {e.pos} | line {e.lineno} col {e.colno}")
lo, hi = max(0, e.pos - 30), min(len(text), e.pos + 30)
print("...", text[lo:hi].replace("\n", "\\n"), "...")
sys.exit(1)
strict_check(open(sys.argv[1], encoding="utf-8").read())
यह jq . से सख़्त है (अग्रणी शून्य और BOM अस्वीकार करता है) और e.pos देता है। line 1 column 23611 को बाइट स्थिति में कैसे बदलें और CJK दस्तावेज़ों में वर्ण-ऑफ़सेट व बाइट-ऑफ़सेट तीन गुना क्यों अलग पड़ते हैं, यह Minified JSON को डिबग करना में विस्तार से है, यहाँ नहीं दोहराते।
5. सुधार कैसे करें: फ़ॉर्मैटिंग को «सख़्त जाँच» + «सजावट» में बाँटें
निष्कर्ष सरल है: एक «फ़ॉर्मैट» बटन को जाँच और सजावट दोनों एक साथ न सौंपें। पहले सख़्त पार्सर से वैधता तय करें, फिर प्रस्तुति के लिए टूल चुनें। पहला चरण एक पंक्ति से अलग किया जा सकता है:
python3 -m json.tool input.json > /dev/null && echo "syntax OK" || echo "invalid syntax"
python3 -c "import json,sys; d=json.load(open(sys.argv[1],encoding='utf-8')); print(type(d).__name__, len(d))" input.json
पहली पंक्ति सिर्फ «पार्स हो सकता है?» का उत्तर देती है, दूसरी जाँचती है «पार्स होकर जो बना वह अपेक्षित संरचना है?». अगर डेटा में बड़े पूर्णांक हैं, Python या jq से पढ़ें, JSON.parse से नहीं; अगर स्रोत में BOM हो सकता है, पढ़ते समय encoding='utf-8-sig' स्पष्ट दें, टूल के अपने आप छोड़ने पर भरोसा न करें; अगर कुंजियाँ दोहर सकती हैं, पार्स से पहले अलग से स्कैन करें, क्योंकि चारों रास्ते आपको चेतावनी नहीं देते। इसे CI में लिखना बिलकुल वही पाइपलाइन है जो एडिटर, कमांड लाइन और ऑटोमेशन टूलचेन समझाता है, बस डेटा-निष्ठा के assertion की एक परत ज़्यादा है।
6. कैसे चुनें: चार रास्तों की तुलना तालिका
| रास्ता | मज़बूती | जोखिम | उपयुक्त |
|---|---|---|---|
python json.loads / json.tool |
सख़्त, पंक्ति/स्तंभ व वर्ण-ऑफ़सेट देता है, पूर्णांकों की सटीकता नहीं खोती | अग्रणी शून्य व BOM अस्वीकार, एन्कोडिंग स्पष्ट संभालनी पड़ती है | अनुबंध जाँच, CI, लॉग विश्लेषण |
node JSON.parse |
ब्राउज़र में मूल, त्रुटि के साथ मूल अंश जुड़ा | बड़े पूर्णांक मौन रूप से कटते हैं, BOM से त्रुटि | फ़्रंटएंड डिबग, ब्राउज़र टूल |
jq -c . |
पाइप के अनुकूल, सबसे तेज़ परिणाम | अग्रणी शून्य व BOM मौन रूप से गुज़रते हैं, वर्ण-ऑफ़सेट नहीं | संचालन पाइपलाइन, तेज़ फ़ील्ड निकासी |
ब्राउज़र टूल /parse, /editor |
वृक्ष दृश्य, कुंजी क्रम, पाथ कॉपी, बिना इंस्टॉल | व्यवहार पृष्ठ के पार्सर पर निर्भर, एक बार जाँचना ज़रूरी | मैनुअल देखना, तात्कालिक त्रुटि खोज |
चुनाव की कुंजी «किसमें ज़्यादा फ़ीचर हैं» नहीं, बल्कि «क्या यह टूल मेरे डेटा के बारे में झूठ बोलता है?» है। ऑर्डर नंबर, राशि या डिवाइस ID वाले डेटा के लिए Python या स्पष्ट रूप से डेटा-निष्ठा घोषित करने वाला टूल चुनें; सिर्फ संरचना देखनी हो, बिना संग्रहण के, ब्राउज़र सबसे सस्ता है। इस साइट का /parse ब्राउज़र में ही सिंटैक्स जाँच और फ़ॉर्मैट करता है — «पहले देख लें कि दिखता कैसा है» के लिए उपयुक्त, पर बड़े पूर्णांकों पर ऊपर वाली स्क्रिप्ट से एक बार मिला लेना अच्छा है।
7. गोपनीयता सिर्फ एक सीमा है
«क्या मेरा डेटा अपलोड होगा?» ऑनलाइन टूल पर सबसे पूछा जाने वाला सवाल है, पर यह «क्या मेरा डेटा बदलेगा?» से स्वतंत्र है: स्थानीय, ऑफ़लाइन चलने वाला टूल भी 20-अंकीय पूर्णांक काट सकता है; उल्टा, सख़्त त्रुटि देने वाला ऑनलाइन टूल मूल टेक्स्ट सर्वर पर POST कर सकता है। दोनों को अलग-अलग जाँचना होगा, एक दूसरे का विकल्प नहीं। गोपनीयता FAQ दो मिनट की तीन जाँचें देता है; यह लेख दूसरा हिस्सा जोड़ता है — नीचे की नमूना सूची से पक्का करें कि टूल मूल्य चुपचाप न बदले।
8. एक कॉपी करने योग्य स्व-जाँच सूची
S1–S10 को cases.txt में रखें, एक पंक्ति एक नमूना, किसी भी उम्मीदवार टूल में चिपकाएँ और तीन बातें मिलाएँ: जिन नमूनों को त्रुटि देनी थी क्या उन्होंने सच में दी, त्रुटि ने ठोस स्थिति बताई या नहीं, सफल नमूने का आउटपुट इनपुट से अक्षर-दर-अक्षर मेल खाता है या नहीं। खासकर S4, S6, S7, S8 देखें — एक भी SILENT पंक्ति मतलब यह रास्ता अकेले डेटा मान्यता के लिए नहीं चल सकता।
तीन इंजीनियरिंग नियम जोड़ें: सख़्त जाँच को pre-commit हुक में डालें, production पर जाकर पूर्णांक बेमेल न पकड़ें; फ़ाइलें सभी भाषाओं में UTF-8 बिना BOM लिखें; तीसरे पक्ष के JSON का कच्चा response body सुरक्षित रखें और फ़ॉर्मैट किया परिणाम सिर्फ पढ़ने के लिए इस्तेमाल करें, वापस लिखने के लिए नहीं।
अक्सर पूछे जाने वाले प्रश्न
फ़ॉर्मैटिंग सफल हुई, क्या इसका मतलब JSON ठीक है?
नहीं। S4, S6, S7, S8 सब «पास» हुए, पर अग्रणी शून्य, BOM, 20-अंकीय पूर्णांक और डुप्लिकेट कुंजी बदल या निगल दी गईं। फ़ॉर्मैटिंग सिर्फ यह साबित करती है कि किसी पार्सर ने टेक्स्ट स्वीकार किया, यह नहीं कि मूल्य आपके भेजे गए मूल्यों के बराबर हैं; महत्वपूर्ण फ़ील्ड मूल्य-दर-मूल्य जाँचें।
jq अग्रणी शून्य और BOM क्यों स्वीकार करता है जिन्हें Python अस्वीकार करता है?
jq का अपना lexer है, 01 जैसी संख्याओं पर नरम है और शुरुआती BOM छोड़ देता है; Python और Node RFC 8259 सख़्ती से मानते हैं। यह वर्ज़न बग नहीं, सहिष्णु और सख़्त पार्सर के बीच का चुनाव है। सफ़ाई में पहला काम बचाता है, अनुबंध जाँच में दूसरा अनिवार्य है।
बड़े पूर्णांक को 9007199254740992 बनने से कैसे रोकें?
JavaScript का Number IEEE 754 double है जिसकी सुरक्षित ऊपरी सीमा 2^53-1 है। 20-अंकीय ID को JSON.parse से पढ़ना अनिवार्य रूप से काटता है। ऐसे ID स्ट्रिंग में रखें या Python int, jq, अथवा BigInt सहायक पार्सर इस्तेमाल करें; अगर फ़्रंटएंड को सच में संख्या चाहिए, पार्स से पहले फ़ील्ड को स्ट्रिंग से बदल दें।
ऑनलाइन टूल या स्थानीय कमांड लाइन, कौन सा लें?
संवेदनशीलता और उद्देश्य से बाँटें: सिर्फ संरचना देखनी या फ़ील्ड ढूँढ़नी हो तो ब्राउज़र सबसे तेज़; पाइपलाइन में जाना और डेटा-निष्ठा के assertion चाहिए तो python -m json.tool प्लस सख़्त जाँच। दोनों में पहले S1–S10 चलाएँ, त्रुटियाँ और आउटपुट अपेक्षा से मिलाएँ, तब तय करें कि किस श्रेणी का डेटा अंदर डालना है।