JSON Toolkit

একই JSON, চারটি টুল, চারটি ফলাফল: ফরম্যাটার কীভাবে বেছে নেবেন

সংক্ষেপে: ফরম্যাটিং ক্ষতিহীন নয়। শূন্য-শুরু সংখ্যা, BOM বা ২০-অঙ্কের পূর্ণসংখ্যাযুক্ত একই JSON কে Python, Node, jq ও json.tool এ দিলে কেউ ত্রুটি দেয়, কেউ নীরবে মান বদলে দেয়। নিজে চালানো দশটি নমুনা ও চারটি পথ থেকে পাওয়া যায় একটি নির্বাচন ছক ও অনুলিপিযোগ্য কঠোর যাচাই স্ক্রিপ্ট।

Intermediate~6 মিনিট পড়াপ্রকাশ 2026-11-03আপডেট 2026-11-03
একই JSON-এর জন্য চারটি বিশ্লেষণ পথ আলাদা ফলাফল দেখাচ্ছে

সিদ্ধান্ত: 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 হিসেবে অদ্বিতীয় চায়, ডুপ্লিকেট কী কীভাবে সামলাবে তা নির্ধারণ না করেই; ধারা ৬ সংখ্যা নিয়ে কেবল আন্তঃকার্যকারিতার সুপারিশ দেয়। ECMA-404 আরও এগিয়ে গিয়ে স্পষ্ট লেখে যে এটি «সংখ্যার অর্থ সম্পর্কে উদাসীন» — শুধু অঙ্ক-ক্রম লেখার নিয়ম দেয়, ভাষায় সংরক্ষণের নয়। কথাটা দাঁড়ায়, স্পেসিফিকেশন এক বিস্তৃত প্রান্তিক এলাকা বাস্তবায়নকারীর হাতে ছেড়ে দেয়।

তাই «ফরম্যাটিং» দুই স্তরে ভাগ হয়: সিনট্যাক্স স্তর ঠিক করে টেক্সট বৈধ কি না, অর্থ স্তর ঠিক করে সেই টেক্সট কোন মানে রূপ নেবে। প্রথমটির মানক উত্তর আছে, দ্বিতীয়টির নেই। jq-র 01 কে 1 পড়া ও শুরুর BOM এড়িয়ে যাওয়া অর্থ স্তরের সিদ্ধান্ত; Node-এর ২০-অঙ্কের পূর্ণসংখ্যাকে IEEE 754 double-এ ঢোকানো runtime-এর সিদ্ধান্ত; চার পথেই {"a":1,"a":2}-এ শেষ মানটি রাখা বড় পার্সারদের সাধারণ সমঝোতা। কোনোটিই «পার্স করা যায়» লঙ্ঘন করে না, অথচ সবাই ডেটা বদলায়।

JSON পার্স এরর ঠিক করার উপায় সিনট্যাক্স স্তরে কাজ করে, ত্রুটির লেখাকে স্থানাঙ্কে ভাষান্তর করে। এই লেখাটি তার ঠিক আগের ধাপটি সামলায় — টুল যদি একেবারেই ত্রুটি না দেয়, তবে আপনি স্থানাঙ্কও পাবেন না।

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 থাকে, শেষ অঙ্ক বদল মানে হিসাব মেলা বন্ধ।

ডুপ্লিকেট কী হলো «স্পেসিফিকেশন চুপ, বাস্তবায়ন নিজে বেছে নেয়» ধরনের। চার পথই কোনো সতর্কতা ছাড়াই নীরবে শেষ মান নেয়। 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-এ লাইন ও কলাম প্রায় অকেজো — সব কিছু লাইন ১-এ। দরকার অক্ষর-অফসেট। নিচের স্ক্রিপ্ট যাচাইকে «ত্রুটি + অফসেট + প্রসঙ্গ»-এ গুছিয়ে দেয়:

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 নথিতে অক্ষর-অফসেট ও বাইট-অফসেট তিন গুণ কেন আলাদা হয়, তা মিনিফায়েড 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. গোপনীয়তা কেবল একটি সীমা

«আমার ডেটা কি আপলোড হবে?» অনলাইন টুলে সবচেয়ে বেশি জিজ্ঞাসিত প্রশ্ন, কিন্তু এটি «আমার ডেটা কি বদলাবে?» থেকে স্বাধীন: স্থানীয়, অফলাইন চলা টুলও ২০-অঙ্কের পূর্ণসংখ্যা কেটে দিতে পারে; উল্টোদিকে, কঠোর ত্রুটি দেওয়া অনলাইন টুল মূল টেক্সট সার্ভারে 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, ২০-অঙ্কের পূর্ণসংখ্যা ও নকল কী বদলে বা গিলে ফেলা হয়েছে। ফরম্যাটিং শুধু প্রমাণ করে যে কোনো এক পার্সার টেক্সট গ্রহণ করেছে, এটি প্রমাণ করে না যে মানগুলো আপনার পাঠানো মানের সমান; গুরুত্বপূর্ণ ফিল্ড মানে মানে যাচাই করুন।

jq অগ্রণী শূন্য ও BOM কেন গ্রহণ করে যা Python ফেরত দেয়?

jq-র নিজস্ব lexer আছে, 01-এর মতো সংখ্যায় নম্র, এবং শুরুর BOM এড়িয়ে যায়; Python ও Node কঠোরভাবে RFC 8259 মানে। এটি ভার্শন বাগ নয়, সহনশীল ও কঠোর পার্সারের মধ্যেকার পছন্দ। পরিচ্ছন্নতায় প্রথমটি শ্রম বাঁচায়, চুক্তি যাচাইয়ে দ্বিতীয়টি আবশ্যক।

বড় পূর্ণসংখ্যাকে 9007199254740992 হতে কীভাবে আটকাবেন?

JavaScript-এর Number হলো IEEE 754 double, নিরাপদ ঊর্ধ্বসীমা 2^53-1। ২০-অঙ্কের ID JSON.parse দিয়ে পড়লে অনিবার্যভাবে কাটা পড়ে। এমন ID স্ট্রিং হিসেবে রাখুন বা Python int, jq কিংবা BigInt-সমর্থিত পার্সার ব্যবহার করুন; ফ্রন্টএন্ডে সত্যিই সংখ্যা লাগলে পার্সের আগে ফিল্ডটি স্ট্রিং দিয়ে বদলান।

অনলাইন টুল না স্থানীয় কমান্ড লাইন, কোনটা নেব?

সংবেদনশীলতা ও উদ্দেশ্য দিয়ে ভাগ করুন: শুধু কাঠামো দেখা বা ফিল্ড খোঁজা হলে ব্রাউজার দ্রুততম; পাইপলাইনে যেতে ও ডেটা-বিশ্বস্ততার assertion চাইলে python -m json.tool প্লাস কঠোর যাচাই। দুটোতেই আগে S1–S10 চালান, ত্রুটি ও আউটপুট প্রত্যাশার সঙ্গে মেলান, তারপর ঠিক করুন কোন শ্রেণির ডেটা ভেতরে দেবেন।