JSON, YAML, CSV ve XML arasında dönüştürürken neler kaybolur
Veri biçimleri arasındaki her dönüşüm, kaynağın ifade edebildiği ama hedefin edemediği şeyi düşürür. Bazı kayıplar bellidir: YAML JSON'a dönüştüğü anda yorumlar buhar olur. Çoğu ise sessizdir. Bir tarih dize olarak gelir, bir dizi beş sütuna dönüşür, 3 ile biten bir kimlik artık 2 ile biter. İşe yarayan alışkanlık, hangi ayrıntının gideceğini o gitmeden önce bilmektir.
Her biçim neyi taşıyabilir
| Yorumlar | Tipler | İç içelik | Tarihler | Anahtar sırası | Yinelenen anahtarlar | |
|---|---|---|---|---|---|---|
| JSON | Yok | string, number, boolean, null, object, array | Var | Yerleşik bir tip yok | Şartnameye göre sırasız, çoğu ayrıştırıcı korur | Tanımsız, genellikle sonuncusu kazanır |
| YAML | Var, # | JSON'unkiler, 1.1'de ayrıca zaman damgaları ve fazlası | Var | 1.1'de var, 1.2'de etikete bağlı | Dosyada korunur, ayrıştırmadan sonra korunmaz | Geçersiz, ama sık sık kabul edilir |
| CSV | Yok | Yok, her hücre metindir | Yok, yalnızca düz satırlar | Yok | Başlık satırıyla sabitlenir | Geçerli ve belirsiz |
| XML | Var, <!-- --> | Şema olmadan yok, XSD ile tiplenir | Var | Yalnızca bir şema üzerinden | Öğeler sıralı, öznitelikler değil | Yinelenen öğeler normal, yinelenen öznitelikler hata |
| TOML | Var, # | string, integer, float, boolean, date, time, date-time, array, table | Var, tablolar aracılığıyla | Var, birinci sınıf | Anlam taşımaz | Her zaman hata |
Sürprizlerin çoğunu iki sütun üretir. Tipler, 007 değerinin sağ kalıp kalmayacağına ya da 7 sayısı olarak varıp varmayacağına karar verir; iç içelik ise bir gelenek uydurmadan CSV'ye dönüşümün mümkün olup olmadığına karar verir.
CSV düzdür ve her hücre metindir
CSV'nin tek bir biçimi vardır: alanlardan oluşan satırlar. Bir hücrede nesne ifade etmenin yolu yoktur, dolayısıyla bir dönüştürücünün bir şey uydurması gerekir. Genellikle noktalı yollarla düzleştirir, yani şu:
[{ "id": 1, "name": { "first": "Ada" }, "tags": ["admin", "ops"] }]
şuna dönüşür:
id,name.first,tags.0,tags.1
1,Ada,admin,ops
Bu, iki şey olana kadar temiz biçimde geri açılır. Farklı uzunluktaki diziler, başlığı en uzun kayda göre genişletir; dolayısıyla üç etiketi olan tek bir satır bir tags.2 sütunu ekler ve diğer bütün satırlarda boş bir hücre bırakır. Ayrıca zaten nokta içeren bir anahtar, sonradan iç içe bir yoldan ayırt edilemez, dolayısıyla dönüş yolculuğu yanlış yapıyı kurar. Diğer seçenek hücrenin içine bir JSON dizesi koymaktır; CSV'nin gerektirdiği gibi tırnakları ikiye katlanmış olarak "[""admin"",""ops""]" biçiminde. Bu hiçbir şey kaybettirmez ve akışın ilerisinde hiçbir şey bunu anlamaz.
Tipler işin ikinci yarısıdır. Bir hücre karakter tutar, dolayısıyla okuyanın tahmin yürütmesi gerekir. Tahmin yürütmek 007 değerini 7 yapar, 1.0 ile 1 değerini tek bir değere indirir, bir posta kodunun başındaki sıfırı düşürür, TRUE olan bir şirket kodunu boolean'a çevirir. Tahmin yürütmemek ise her sayıyı dize olarak verir. CSV'den JSON'a ya da TSV'den JSON'a dönüşümdeki tip çıkarımı bir kurtarma değil, makul varsayılanlarla yapılan bir tahmindir, çünkü özgün tipler hiçbir zaman yazıya dökülmemiştir.
Boş bir hücre boş dize, null, sıfır ya da geçerli değil anlamına gelebilir; ne anlama geldiğine karar verin ve bunu söyleyin, çünkü dosya söyleyemez. Ayırıcıları değiştirmek, yani CSV'den TSV'ye geçmek, buradaki tek güvenli dönüşümdür: zarar görecek bir tip yoktur.
Tipi sizin yerinize YAML tahmin eder
YAML tipleri tırnaksız metinden çıkarır; onu yazması keyifli, dönüştürmesi riskli yapan da budur.
country: no
version: 1.20
build: 010
start: 12:30
PyYAML'ın ve birçok eski kütüphanenin hâlâ uyguladığı bir YAML 1.1 ayrıştırıcısında bu şu demektir:
{ "country": false, "version": 1.2, "build": 8, "start": 750 }
Dördünün de anlamı değişti. no meşhur Norveç sorunudur: YAML 1.1, y, yes, no, on ve off değerlerini boolean sayar, dolayısıyla ülke kodu false olur. 1.20 bir float'tır, dolayısıyla sondaki sıfır gitmiştir. 010 sekizlik kalıba uyar ve 8 olur. 12:30 altmışlık kalıba uyar ve dakika sayısı olan 750'ye dönüşür.
Güncel js-yaml gibi bir YAML 1.2 çekirdek şema ayrıştırıcısı ise aynı dosyayı "no" dizesi, 010 için 10 ve "12:30" dizesi olarak okur. Tek dosya, iki ayrıştırıcı, farklı veri. Şartnameler gerçekten anlaşamıyor; 1.2 altmışlık ve boolean sözcük kurallarını bilerek kaldırdı.
Savunma tek bir karakterdir. Metin olması gereken her şeyi tırnaklayın: country: "no", version: "1.20". YAML ile JSON dönüştürücüsü, bir ayrıştırıcının dosyadan ne anladığını gösterir, çünkü JSON'da belirsizliğin saklanacak yeri yoktur.
JSON'da tarih yok, yorum yok ve 53 bitlik bir tavan var
JSON sayıları IEEE 754 çift duyarlıklı sayılardır, dolayısıyla tam sayılar yalnızca 2^53'e, yani 9007199254740992'ye kadar tam kalır. Bunun ötesinde:
{ "id": 9007199254740993 }
Bunu çoğu dilde ayrıştırıp yeniden serileştirin, 9007199254740992 olarak geri döner. Değer hiçbir şekilde temsil edilemez, dolayısıyla yuvarlanmış olmaktan çok elde edilemez durumdadır. Discord, Twitter ve diğer snowflake API'lerinin kimlikleri dize olarak göndermesinin nedeni budur: 64 bitlik bir tam sayı bir JSON sayısına sığmaz.
Bir tarih tipi de yoktur, dolayısıyla tarihler dize olarak, alışıldık biçimde ISO 8601 ile yolculuk eder ve hiçbir şey "2026-08-25" değerini metin değil tarih olarak imlemez. TOML üzerinden ya da bir YAML 1.1 ayrıştırıcısından geçerken gerçek bir tarihe dönüşebilir, sonra da farklı biçimlenmiş olarak geri gelir. JSON'da ayrıca yorum ve sondaki virgül de yoktur, dolayısıyla bir YAML ya da TOML kaynağındaki açıklamalar taşınmaz, yok olur. Bir örnekten TypeScript arayüzleri üretmek bu iki kör noktayı da devralır: bir dize tarih değildir ve olmayan bir alan isteğe bağlı demek değildir.
XML, iki yönde de JSON'un içine sığmaz
XML öznitelikleri alt öğelerden ayırır, JSON'da ise yalnızca anahtar vardır.
<user id="7" active="true">
<name>Ada</name>
<tag>admin</tag>
<tag>ops</tag>
</user>
Bir dönüştürücü kendine bir gelenek seçer; genellikle özniteliklerin başına @ koyar ve yinelenen öğeleri bir dizide toplar. Bu, belirli bir biçimde kayıplıdır: tek bir <tag> olduğunda dizi oluşmaz, dolayısıyla çıktının biçimi şemaya değil veriye bağlı olur ve liste bekleyen kod, tek etiketi olan kayıtta kırılır. <p>Hello <b>there</b> friend</p> gibi karışık içeriğin JSON'da karşılığı yoktur; ad alanları, CDATA ve yorumlar da kaybolur.
Ters yön başka şeyler kaybettirir. Öğe adları rakamla başlayamaz ve boşluk içeremez, dolayısıyla rastgele JSON anahtarlarının biçimi bozulmak zorundadır. null değeri gelenek gereği boş bir öğeye ya da xsi:nil özniteliğine dönüşür. En üst düzeydeki bir dizi, kaynakta hiç bulunmayan bir sarmalayıcı öğe gerektirir. JSON'da ayrıca bir değerin bir zamanlar öznitelik olup olmadığını kaydeden hiçbir şey yoktur, dolayısıyla JSON'dan XML'e dönüşüm yalnızca öğe üretir. CSV'den XML'e dönüşüm ise bu seçimi açık açık yapar ve size öğe mi öznitelik mi istediğinizi sorar.
Anahtar sırası ve yinelenen anahtarlar
Anahtar sırası hiçbir yerde garanti edilmez ve neredeyse her yerde korunur. JSON şartnamesi nesneleri sırasız sayar, yine de bütün yaygın ayrıştırıcılar ekleme sırasını korur. Karşılaştırılan ya da sağlama toplamı alınan her şey için anahtarları sıralamak yine de daha güvenlidir, çünkü anahtarları yalnızca yer değiştirmiş iki belgenin farkı okunmaz haldedir. JSON karşılaştırma aracının yaptığı gibi yapısal karşılaştırma yapmak, eşleştirmeyi konuma göre değil anahtara göre yapar ve soruyu tümüyle ortadan kaldırır.
Yinelenen anahtarlar daha keskin bir kenardır. JSON hiçbir şey söylemez, dolayısıyla ayrıştırıcılar ayrışır ve çoğu sessizce son değeri tutar. YAML bunları yasaklar ve birçok ayrıştırıcı yine de kabul eder. TOML düpedüz reddeder. CSV aynı başlığa sahip iki sütuna izin verir ve kararı okuyana bırakır; bu da tanımadığınız bir dosyayı önce bir CSV doğrulayıcıdan geçirmek için yeterli sebeptir. Tekrarın kaza değil anlam taşıdığı tek biçim XML'dir.
Bilerek dönüştürmek
Her boru hattında kayıplı bir adım vardır. Hangisi olduğunu sonradan keşfetmek yerine seçin.
- En dar biçime en sonda varın. XML'den JSON'a, oradan CSV'ye giden yol ilk adımda öznitelikleri, ikincisinde iç içeliği kaybeder. Hedef CSV ise hangi alanların önemli olduğuna karar verin ve bilerek düzleştirin.
- Dönüştürmeden önce tırnaklayın, sonra değil. Sürüm numaraları, ülke kodları, posta kodları ve başında sıfır olan her şey kaynakta tırnak içinde olmalıdır.
nobir kezfalseolduğunda özgün metin geri getirilemez. - Çıktıyı veri olarak denetleyin. Biçimlendirin, kayıt sayısını doğrulayın ve bilinen zorlu bir satıra bakın: addaki virgül, en uzun dizi, en büyük kimlik.
- Bir gidiş dönüşün farkını alın. Dönüştürüp geri dönüştürün, karşılaştırın. Karşılaştırmanın bildirdiği şey, dönüşümün taşıyamadığı şeydir.
- Özgün dosyayı saklayın. Dönüştürülmüş dosya bir türevdir ve üç ay sonra biri bir alanın boş mu yoksa hiç yok mu olduğunu sorduğunda yalnızca kaynak cevap verebilir.