Batas Konversi JSON ↔ YAML dan XML
Intinya: Klaim bahwa JSON adalah subset YAML hanya menjamin satu arah, dan XML tidak memiliki pemetaan JSON resmi sama sekali — semuanya bersandar pada konvensi.

Intinya: gunakan JSON sebagai format perantara: parse sumbernya menjadi model data, lalu serialisasi ke target; sebelum mengonversi, tentukan apakah komentar, atribut XML dan namespace, serta tipe numerik atau boolean harus bertahan.
Apakah pipeline konversi dapat digunakan tidak bergantung pada seberapa cantik outputnya, melainkan pada apakah Anda menyadari berapa banyak yang telah dibuangnya.
1. JSON dan YAML: penampungan satu arah
Spesifikasi YAML 1.2 menyatakannya dengan jelas: JSON adalah subset dari YAML. Artinya, JSON valid apa pun adalah YAML valid:
{"name": "json", "tags": ["a", "b"], "count": 3}
Cuplikan ini adalah JSON sekaligus YAML flow style. Sebaliknya tidak berlaku.
| Fitur | JSON | YAML 1.2 |
|---|---|---|
| Komentar | ✗ | ✓ |
| Anchor dan alias | ✗ | ✓ |
Beberapa dokumen (---) |
✗ | ✓ |
| Kunci non-string | ✗ | ✓ (1: a, true: b) |
| Kunci kompleks | ✗ | ✓ (? [a, b]) |
| Sensitif indentasi | ✗ | ✓ (indentasi tab ditolak) |
| Inferensi tipe | eksplisit | implisit (0o17, 0x1F, .inf) |
| Keterbacaan satu baris | baik | buruk (konten panjang bergantung pada sintaks folding) |
1.1 Inferensi tipe adalah jebakan terbesar
YAML mem-parsing sebagian nilai yang tampak seperti string menjadi tipe lain:
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
Contoh NO telah membakar banyak orang: dengan parser YAML 1.1 (sebagian pustaka lama), country: NO menjadi false.
Penangkalnya: kutip setiap pengenal yang "tampak seperti angka atau boolean", seperti version: "1.10".
1.2 Anchor, alias, dan beberapa dokumen
defaults: &defaults
retries: 3
timeout: 30
serviceA:
<<: *defaults
host: a.example.com
---
serviceB:
<<: *defaults
host: b.example.com
Setelah memperluas anchor dan mengonversi ke JSON, Anda biasanya berakhir dengan dua object konfigurasi terduplikasi, atau dengan satu array (beberapa dokumen harus menjadi [{...}, {...}]).
Either way, anchor tidak dipulihkan pada konversi balik — kehilangan itu tidak dapat dihindari.
1.3 Konversi yang mempertahankan komentar
Sebagian besar pustaka membuang komentar begitu saja selama konversi. Saat Anda perlu mempertahankannya, gunakan Document API dari paket yaml:
import { parseDocument } from 'yaml';
const document = parseDocument(`
# Service configuration
port: 8080
`);
document.set('port', 3000);
console.log(document.toString());
// # Service configuration
// port: 3000
Ujiannya sederhana: jika file ini akan diedit manusia berulang kali, jangan mengambil jalur "parse → object → serialize".
1.4 Cara memverifikasi konsistensi round-trip
/**
* Verifies round-trip consistency for JSON → YAML → JSON.
* @param sourceValue Original JSON value
* @param stringify YAML serialization function
* @param parse YAML parsing function
* @returns Whether the two results are exactly equal
*/
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);
}
Menjalankannya terhadap data nyata lebih bernilai daripada membaca dokumentasi sepuluh kali. Pastikan set sampel Anda mencakup: null, string kosong, array kosong, "00", "1e5", nol negatif, integer yang lebih besar dari 2^53, dan string panjang berisi baris baru.
2. JSON dan XML: tidak ada pemetaan standar
XML mendahului JSON jauh lebih lama dan memiliki kemampuan struktural yang jauh lebih kuat (atribut, namespace, mixed content, DTD). Di antara keduanya tidak ada spesifikasi pemetaan resmi; setiap alat melakukan hal yang sama: memilih konvensi, lalu berusaha tidak kehilangan banyak.
2.1 Konvensi umum
| Konvensi | Representasi | Contoh |
|---|---|---|
| Elemen → kunci object | Elemen bernama sama menjadi field | <a>1</a> → {"a": "1"} |
Atribut → prefiks @ |
@name |
<a id="1"/> → {"a": {"@id": "1"}} |
Teks → #text |
Ketika ada atribut sekaligus teks | <a id="1">hi</a> → {"a": {"@id": "1", "#text": "hi"}} |
| Elemen berulang → array | Dua kemunculan atau lebih menjadi array | <i>1</i><i>2</i> → {"i": ["1","2"]} |
| Namespace → prefiks | 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": "钢笔" }
]
}
}
2.2 Lima kehilangan yang tidak dapat dihindari
- Urutan elemen: object JSON tidak berurutan, sementara XML adalah urutan elemen yang berurutan;
- Atribut vs elemen anak: keduanya membawa semantik berbeda di XML, tetapi keduanya menjadi field biasa di JSON;
- Mixed content: struktur seperti
<p>你好 <b>世界</b> 再见</p>tidak dapat diekspresikan dengan object biasa; - Deklarasi namespace:
xmlnsadalah konsep scoping; setelah diratakan menjadi field, scopenya hilang; - CDATA dan entity:
<![CDATA[...]]>dan&tidak lebih dari teks di JSON.
2.3 Dilema "satu elemen atau array"
<list><item>1</item></list>
Dengan satu item, xml2js mengembalikan object secara default dan hanya mengembalikan array setelah ada dua.
Akibatnya, kode front-end harus menulis Array.isArray(x) ? x : [x].
Penangkalnya: aktifkan opsi "always array" saat parsing, atau deklarasikan field sebagai tipe array berdasarkan schema.
import { parseStringPromise } from 'xml2js';
const parsed = await parseStringPromise(xmlText, {
explicitArray: true, // return an array even for a single element
trim: true,
explicitRoot: false,
});
2.4 Pengingat keamanan
Sebelum mem-parsing XML tak tepercaya Anda harus menonaktifkan external entity, jika tidak Anda dapat memicu XXE (membaca file lokal, memindai jaringan internal):
// In the Node ecosystem, DTDs and external entities are usually disabled through parser options
const parserOptions = { doctype: false, externalEntities: false };
XML → JSON hanyalah lapisan pertama konversi; mengonversi tidak menghilangkan permukaan serangan yang dibawa XML.
3. Di mana masing-masing dari tiga format cocok
| Skenario | Format yang disarankan | Alasan |
|---|---|---|
| API HTTP publik | JSON | Didukung secara native oleh browser dan setiap bahasa |
| Konfigurasi yang dipelihara tangan | YAML | Komentar, indentasi, keterbacaan |
| Dokumen yang membutuhkan schema kuat dan namespace (SOAP, Office Open XML, SVG) | XML | Kemampuan struktural dan ekosistem yang sudah ada |
| Interaksi dengan perkakas operasional (K8s, CI) | YAML | Default ekosistem |
| Persistensi, cache, log | JSON / JSONL | Universal dan tanpa dependensi |
4. Catatan implementasi
import { stringify as toYaml } from 'yaml';
/**
* Converts a JSON value to YAML text while keeping Chinese characters unescaped.
* @param value The JSON value to convert
* @returns The YAML text
*/
function jsonToYaml(value: unknown): string {
return toYaml(value, {
indent: 2,
lineWidth: 0, // disable automatic wrapping so long strings are not split
defaultStringType: 'QUOTE_DOUBLE',
});
}
Beberapa opsi praktis:
lineWidth: 0mematikan pembungkusan otomatis; jika tidak, URL panjang terpecah ke beberapa baris dan menjadi sulit dibaca;- mengutip semuanya dengan kutip ganda mencegah nilai seperti
NOdan1.10disalahbaca; - untuk data dengan presisi numerik sensitif, konversi ke string sebelum serialisasi.
Untuk arah XML, tetapkan satu fungsi pemetaan tunggal dan letakkan "prefiks atribut", "nama kunci teks", dan "strategi array" ke dalam signature-nya alih-alih menyebarkannya:
type XmlMappingOptions = {
attributePrefix: string;
textKey: string;
alwaysArray: boolean;
};
const defaultMapping: XmlMappingOptions = {
attributePrefix: '@',
textKey: '#text',
alwaysArray: false,
};
5. Daftar periksa
- [ ] Setiap pengenal "mirip angka/boolean" di YAML dikutip;
- [ ] Set sampel validasi untuk JSON → YAML → JSON didefinisikan dan dicakup tes;
- [ ] Saat komentar harus dipertahankan, Document API digunakan alih-alih round trip object;
- [ ] Konvensi pemetaan XML dituliskan dan tim berbagi satu set parameter;
- [ ] DTD dan external entity dinonaktifkan saat mem-parsing XML tak tepercaya;
- [ ] Jumlah byte dan jumlah field dibandingkan sebelum dan sesudah konversi, dan anomali apa pun memicu peringatan.
FAQ
Bagaimana cara memilih arah konversi antara JSON, YAML, dan XML?
Utamakan JSON sebagai format perantara: parse YAML dan XML menjadi model data lebih dulu, lalu serialisasi dari model itu alih-alih menulis ulang teks. Gunakan YAML Document API saat komentar harus bertahan, dan tetapkan satu konvensi pemetaan XML sebelum Anda membutuhkan atribut atau namespace.
Mengapa konversi XML ke JSON kehilangan atribut dan namespace?
JSON tidak memiliki konsep atribut atau namespace, sehingga atribut XML, prefiks, dan URI namespace harus dilipat menjadi kunci biasa, umumnya dengan prefiks @ atau node #text. Begitu aturan pemetaan menyimpang, XML yang sama menghasilkan JSON berbeda dan tidak dapat melakukan round-trip.
Haruskah saya mempertahankan JSON, YAML, dan XML dalam sinkronisasi dua arah jangka panjang?
Tidak. Komentar, anchor, atribut XML, dan namespace bersifat lossy lintas format, dan sinkronisasi dua arah terus mengakumulasi perbedaan. Pilih satu format sumber dan perlakukan yang lain sebagai ekspor sekali jalan atau tampilan sementara.