JSON ↔ YAML ও XML রূপান্তরের সীমানা
সংক্ষেপে: JSON কীভাবে YAML-এর সাবসেট — এই দাবি কেবল এক দিক নিশ্চিত করে, আর XML-এর কোনো সরকারি JSON ম্যাপিং নেই — সবই প্রথার উপর নির্ভরশীল।

মূল কথা: JSON-কে মধ্যবর্তী ফরম্যাট হিসেবে ব্যবহার করুন: উৎসকে ডেটা মডেলে পার্স করুন, তারপর লক্ষ্যে সিরিয়ালাইজ করুন; রূপান্তরের আগে ঠিক করুন কমেন্ট, XML অ্যাট্রিবিউট ও নেমস্পেস, এবং সাংখ্যিক বা বুলিয়ান টাইপ টিকতে হবে কি না।
কোনো রূপান্তর পাইপলাইন ব্যবহারযোগ্য কি না তা নির্ভর করে আউটপুট কত সুন্দর দেখায় তার উপর নয়, বরং আপনি জানেন কি না যে এটি কতখানি ফেলে দিয়েছে তার উপর।
১. JSON ও YAML: একমুখী ধারণ
YAML 1.2 স্পেসিফিকেশন স্পষ্ট বলে: JSON হলো YAML-এর একটি সাবসেট। অর্থাৎ যেকোনো বৈধ JSON বৈধ YAML:
{"name": "json", "tags": ["a", "b"], "count": 3}
এই টুকরোটি একইসঙ্গে JSON এবং YAML ফ্লো স্টাইল। উল্টোটা সত্য নয়।
| বৈশিষ্ট্য | JSON | YAML 1.2 |
|---|---|---|
| কমেন্ট | ✗ | ✓ |
| অ্যাঙ্কর ও অ্যালিয়াস | ✗ | ✓ |
একাধিক ডকুমেন্ট (---) |
✗ | ✓ |
| অ-স্ট্রিং কি | ✗ | ✓ (1: a, true: b) |
| জটিল কি | ✗ | ✓ (? [a, b]) |
| ইনডেন্টেশনে সংবেদনশীল | ✗ | ✓ (ট্যাব ইনডেন্টেশন প্রত্যাখ্যাত) |
| টাইপ অনুমান | স্পষ্ট | অন্তর্নিহিত (0o17, 0x1F, .inf) |
| এক-লাইনে পাঠযোগ্যতা | ভালো | খারাপ (লম্বা বিষয়বস্তু ভাঁজ সিনট্যাক্সে নির্ভর করে) |
১.১ টাইপ অনুমানই সবচেয়ে বড় ফাঁদ
YAML স্ট্রিং দেখতে এমন কিছু মানকে অন্য টাইপে পার্স করে:
zip: 01234 # string or octal integer? the YAML 1.2 core schema treats it as a string
version: 1.10 # 1.1 (a float); the semantics are lost
country: NO # a string in YAML 1.2; boolean false in YAML 1.1
date: 2026-10-30 # some implementations parse it as a date object
NO উদাহরণটি অনেককে পুড়িয়েছে: YAML 1.1 পার্সার (কিছু পুরোনো লাইব্রেরি) দিয়ে country: NO হয় false।
প্রতিকার: "সংখ্যা বা বুলিয়ান দেখতে" প্রতিটি আইডেন্টিফায়ার কোট করুন, যেমন version: "1.10"।
১.২ অ্যাঙ্কর, অ্যালিয়াস ও একাধিক ডকুমেন্ট
defaults: &defaults
retries: 3
timeout: 30
serviceA:
<<: *defaults
host: a.example.com
---
serviceB:
<<: *defaults
host: b.example.com
অ্যাঙ্কর খুলে JSON-এ রূপান্তরের পর সাধারণত দুইটি পুনরাবৃত্ত কনফিগারেশন অবজেক্ট বা একটি অ্যারে পাওয়া যায় (একাধিক ডকুমেন্ট [{...}, {...}] হতে হয়)।
যেভাবেই হোক, উল্টো রূপান্তরে অ্যাঙ্কর ফেরে না — এই ক্ষতি অনিবার্য।
১.৩ কমেন্ট সংরক্ষণকারী রূপান্তর
বেশিরভাগ লাইব্রেরি রূপান্তরের সময় কমেন্ট ফেলে দেয়। সংরক্ষণ করতে হলে yaml প্যাকেজের Document API ব্যবহার করুন:
import { parseDocument } from 'yaml';
const document = parseDocument(`
# Service configuration
port: 8080
`);
document.set('port', 3000);
console.log(document.toString());
// # Service configuration
// port: 3000
পরীক্ষাটি সহজ: এই ফাইল বারে বারে মানুষ সম্পাদনা করবে হলে "parse → object → serialize" পথ নেবেন না।
১.৪ রাউন্ড-ট্রিপ সামঞ্জস্য কীভাবে যাচাই করবেন
/**
* JSON → YAML → JSON-এর রাউন্ড-ট্রিপ সামঞ্জস্য যাচাই করে।
* @param sourceValue মূল JSON মান
* @param stringify YAML সিরিয়ালাইজেশন ফাংশন
* @param parse YAML পার্সিং ফাংশন
* @returns দুই ফলাফল হুবহু সমান কি না
*/
function isRoundTripStable(
sourceValue: unknown,
stringify: (input: unknown) => string,
parse: (input: string) => unknown,
): boolean {
const yamlText = stringify(sourceValue);
const restored = parse(yamlText);
return JSON.stringify(restored) === JSON.stringify(sourceValue);
}
বাস্তব ডেটার উপর এটি চালানো দশবার ডকুমেন্টেশন পড়ার চেয়ে বেশি মূল্যবান। আপনার নমুনা সেটে অবশ্যই থাকুক: null, খালি স্ট্রিং, খালি অ্যারে, "00", "1e5", ঋণাত্মক শূন্য, 2^53-এর বেশি ইন্টিজার, আর নিউলাইনযুক্ত লম্বা স্ট্রিং।
২. JSON ও XML: কোনো স্ট্যান্ডার্ড ম্যাপিং নেই
XML JSON-এর অনেক আগে এসেছে এবং অনেক শক্তিশালী কাঠামোগত সামর্থ্য রাখে (অ্যাট্রিবিউট, নেমস্পেস, মিশ্র বিষয়বস্তু, DTD)। দুইয়ের মধ্যে কোনো সরকারি ম্যাপিং স্পেসিফিকেশন নেই; প্রতিটি টুল একই কাজ করে: একটি প্রথা বেছে নেয়, তারপর বেশি না হারানোর চেষ্টা করে।
২.১ প্রচলিত প্রথা
| প্রথা | প্রকাশ | উদাহরণ |
|---|---|---|
| এলিমেন্ট → অবজেক্ট কি | একই নামের এলিমেন্ট ফিল্ড হয় | <a>1</a> → {"a": "1"} |
অ্যাট্রিবিউট → @ প্রিফিক্স |
@name |
<a id="1"/> → {"a": {"@id": "1"}} |
টেক্সট → #text |
অ্যাট্রিবিউট ও টেক্সট দুটোই থাকলে | <a id="1">hi</a> → {"a": {"@id": "1", "#text": "hi"}} |
| পুনরাবৃত্ত এলিমেন্ট → অ্যারে | দুই বা বেশি উপস্থিতি অ্যারে হয় | <i>1</i><i>2</i> → {"i": ["1","2"]} |
| নেমস্পেস → প্রিফিক্স | ns:tag |
<ns:tag/> → {"ns:tag": ...} |
<order id="A1001">
<item sku="X1" qty="2">笔记本</item>
<item sku="X2" qty="1">钢笔</item>
</order>
{
"order": {
"@id": "A1001",
"item": [
{ "@sku": "X1", "@qty": "2", "#text": "笔记本" },
{ "@sku": "X2", "@qty": "1", "#text": "钢笔" }
]
}
}
২.২ পাঁচটি অনিবার্য ক্ষতি
- এলিমেন্টের ক্রম: JSON অবজেক্ট ক্রমহীন, অথচ XML এলিমেন্টের ক্রমযুক্ত ধারা;
- অ্যাট্রিবিউট বনাম চাইল্ড এলিমেন্ট: XML-এ তারা আলাদা সেমান্টিক্স বহন করে, কিন্তু JSON-এ দুটোই সাধারণ ফিল্ড হয়;
- মিশ্র বিষয়বস্তু:
<p>你好 <b>世界</b> 再见</p>-এর মতো কাঠামো সাধারণ অবজেক্টে প্রকাশ করা যায় না; - নেমস্পেস ঘোষণা:
xmlnsএকটি স্কোপিং ধারণা; ফিল্ডে ফ্ল্যাট হলে স্কোপ হারায়; - CDATA ও এনটিটি:
<![CDATA[...]]>ও&JSON-এ শুধু টেক্সট ছাড়া কিছু নয়।
২.৩ "এক এলিমেন্ট নাকি অ্যারে" দ্বিধা
<list><item>1</item></list>
একটি item থাকলে xml2js ডিফল্টভাবে অবজেক্ট দেয়, আর দুটি হলেই কেবল অ্যারে দেয়।
ফলে ফ্রন্টএন্ড কোডকে লিখতে হয় Array.isArray(x) ? x : [x]।
প্রতিকার: পার্সের সময় "always array" অপশন চালু করুন, বা স্কিমার ভিত্তিতে ফিল্ডকে অ্যারে টাইপ ঘোষণা করুন।
import { parseStringPromise } from 'xml2js';
const parsed = await parseStringPromise(xmlText, {
explicitArray: true, // একক এলিমেন্টেও অ্যারে ফেরত দিন
trim: true,
explicitRoot: false,
});
২.৪ নিরাপত্তা সতর্কতা
অবিশ্বস্ত XML পার্স করার আগে অবশ্যই বাহ্যিক এনটিটি বন্ধ করুন, নাহলে XXE ট্রিগার হতে পারে (স্থানীয় ফাইল পড়া, অভ্যন্তরীণ নেটওয়ার্ক প্রোব):
// Node ইকোসিস্টেমে সাধারণত পার্সার অপশন দিয়ে DTD ও বাহ্যিক এনটিটি বন্ধ করা হয়
const parserOptions = { doctype: false, externalEntities: false };
XML → JSON রূপান্তরের কেবল প্রথম স্তর; রূপান্তর XML যে আক্রমণ-পৃষ্ঠ আনে তা দূর করে না।
৩. তিন ফরম্যাট কোথায় মানানসই
| পরিস্থিতি | প্রস্তাবিত ফরম্যাট | কারণ |
|---|---|---|
| পাবলিক HTTP API | JSON | ব্রাউজার ও প্রতিটি ভাষায় নেটিভ সমর্থন |
| হাতে রক্ষণাবেক্ষণ করা কনফিগারেশন | YAML | কমেন্ট, ইনডেন্টেশন, পাঠযোগ্যতা |
| শক্ত স্কিমা ও নেমস্পেস দরকার এমন ডকুমেন্ট (SOAP, Office Open XML, SVG) | XML | কাঠামোগত সামর্থ্য ও বিদ্যমান ইকোসিস্টেম |
| অপারেশনাল টুলিংয়ের সঙ্গে মিথস্ক্রিয়া (K8s, CI) | YAML | ইকোসিস্টেমের ডিফল্ট |
| পার্সিস্টেন্স, ক্যাশে, লগ | JSON / JSONL | সর্বজনীন ও নির্ভরতা-মুক্ত |
৪. বাস্তবায়নের টীকা
import { stringify as toYaml } from 'yaml';
/**
* JSON মানকে YAML টেক্সটে রূপান্তর করে, চীনা অক্ষর এস্কেপ না রেখে।
* @param value রূপান্তর করার JSON মান
* @returns YAML টেক্সট
*/
function jsonToYaml(value: unknown): string {
return toYaml(value, {
indent: 2,
lineWidth: 0, // স্বয়ংক্রিয় ভাঁজ বন্ধ করুন যাতে লম্বা স্ট্রিং না ভাঙে
defaultStringType: 'QUOTE_DOUBLE',
});
}
কয়েকটি ব্যবহারিক অপশন:
lineWidth: 0স্বয়ংক্রিয় ভাঁজ বন্ধ করে; নাহলে লম্বা URL লাইনে ভেঙে গিয়ে পড়া কঠিন হয়;- সবকিছু ডাবল কোটে কোট করলে
NOও1.10-এর মতো মান ভুল পড়া থেকে বাঁচে; - সংখ্যার নির্ভুলতা সংবেদনশীল ডেটার ক্ষেত্রে সিরিয়ালাইজের আগে স্ট্রিংয়ে রূপান্তর করুন।
XML দিকের জন্য একটি মাত্র ম্যাপিং ফাংশন স্থির করুন এবং "অ্যাট্রিবিউট প্রিফিক্স", "টেক্সট কি-নাম" ও "অ্যারে কৌশল" এর স্বাক্ষরে রাখুন, চারপাশে ছড়িয়ে দেবেন না:
type XmlMappingOptions = {
attributePrefix: string;
textKey: string;
alwaysArray: boolean;
};
const defaultMapping: XmlMappingOptions = {
attributePrefix: '@',
textKey: '#text',
alwaysArray: false,
};
৫. চেকলিস্ট
- [ ] YAML-এ "সংখ্যা/বুলিয়ান-সদৃশ" প্রতিটি আইডেন্টিফায়ার কোট করা;
- [ ] JSON → YAML → JSON-এর ভ্যালিডেশন নমুনা সেট সংজ্ঞায়িত এবং টেস্টে কভার করা;
- [ ] কমেন্ট সংরক্ষণ করতে হলে অবজেক্ট রাউন্ড ট্রিপের বদলে Document API ব্যবহার;
- [ ] XML ম্যাপিং প্রথা লিখে রাখা এবং দল একই প্যারামিটার সেট শেয়ার করে;
- [ ] অবিশ্বস্ত XML পার্সের সময় DTD ও বাহ্যিক এনটিটি বন্ধ;
- [ ] রূপান্তরের আগে-পরে বাইট ও ফিল্ডের সংখ্যা তুলনা, অস্বাভাবিকতায় সতর্কতা।
সাধারণ প্রশ্ন
JSON, YAML ও XML-এর মধ্যে রূপান্তরের দিক কীভাবে বেছে নেব?
JSON-কে মধ্যবর্তী ফরম্যাট হিসেবে প্রাধান্য দিন: আগে YAML ও XML-কে ডেটা মডেলে পার্স করুন, তারপর টেক্সট আবার লেখার বদলে মডেল থেকে সিরিয়ালাইজ করুন। কমেন্ট টিকতে হলে YAML Document API ব্যবহার করুন, আর অ্যাট্রিবিউট বা নেমস্পেস দরকার হওয়ার আগেই একটি XML ম্যাপিং প্রথা স্থির করুন।
XML থেকে JSON রূপান্তরে অ্যাট্রিবিউট ও নেমস্পেস হারায় কেন?
JSON-এ অ্যাট্রিবিউট বা নেমস্পেসের ধারণা নেই, তাই XML অ্যাট্রিবিউট, প্রিফিক্স ও নেমস্পেস URI সাধারণ কি-তে ভাঁজ করতে হয়, সাধারণত @ প্রিফিক্স বা #text নোড দিয়ে। ম্যাপিং নিয়ম আলাদা হয়ে গেলে একই XML আলাদা JSON দেয় এবং রাউন্ড-ট্রিপ হয় না।
JSON, YAML ও XML দীর্ঘমেয়াদে দ্বিমুখী সিঙ্কে রাখা উচিত?
না। কমেন্ট, অ্যাঙ্কর, XML অ্যাট্রিবিউট ও নেমস্পেস ফরম্যাটজুড়ে লসি, আর দ্বিমুখী সিঙ্কে পার্থক্য জমতেই থাকে। একটি উৎস ফরম্যাট বেছে নিন এবং বাকিগুলোকে এককালীন এক্সপোর্ট বা অস্থায়ী ভিউ ভাবুন।