İçeriğe geç
academia.sh

Ders 20 / 21

JSON

Dizileştirme ve ayrıştırma, biçimin desteklemediği değerler, döngüsel başvuru, dönüşümü uyarlayan geri çağrılar ve sayı duyarlığının sınırı.

İçindekiler

Önceki ders okunabilir bir rapor metni üretti. O metin insan içindi; makinenin geri okuyabileceği bir biçim değil. Bu ders JSON’u ele alıyor: verinin metne çevrilip geri okunması.

JSON, JavaScript sözdiziminden türemiş ama ondan bağımsız bir veri biçimidir. Bu köken, iki dilin birbirine yakın görünmesine ama tam örtüşmemesine yol açar. Ayrımı bilmek, sessiz veri kaybını önler.

Dizileştirme ve Ayrıştırma

const kayit = { istasyon: "A1", sicaklik: 21.5, nem: null, dogrulandi: false };
const metin = JSON.stringify(kayit);
console.log(metin);
console.log(typeof metin);
console.log(JSON.stringify(kayit, null, 2));
const geri = JSON.parse(metin);
console.log(geri, geri.sicaklik + 1);
console.log(kayit === geri);
{"istasyon":"A1","sicaklik":21.5,"nem":null,"dogrulandi":false}
string
{
  "istasyon": "A1",
  "sicaklik": 21.5,
  "nem": null,
  "dogrulandi": false
}
{ istasyon: 'A1', sicaklik: 21.5, nem: null, dogrulandi: false } 22.5
false

JSON.stringify bir değeri metne çevirir; üçüncü argüman girinti miktarıdır ve okunabilir çıktı üretir. JSON.parse metinden yeni bir değer üretir.

Son satır önemlidir: çevrim sonucu yeni bir nesnedir. Eşitlik Karşılaştırmaları dersindeki kimlik kuralı gereği özgün nesneye eşit değildir. Bu, aynı zamanda derin kopya almanın bir yoludur — ancak yalnızca biçimin desteklediği değerler için.

Çıktıdaki üçüncü blok JSON metnidir: anahtarlar çift tırnaklıdır. Sondaki nesne gösterimi ise çalışma zamanının inceleme biçimidir; tırnaksız anahtarlar ve tek tırnaklı metinler oradan gelir. İkisi farklı şeylerdir.

Biçimin Desteklemediği Değerler

JSON’un değer kümesi dilin değer kümesinden dardır. Desteklenmeyen değerler sessizce işlenir:

const kayit = {
  istasyon: "A1",
  eksik: undefined,
  fn: () => 1,
  sembol: Symbol("x"),
  gecersiz: NaN,
  sonsuz: Infinity,
};
console.log(JSON.stringify(kayit));
console.log(JSON.stringify([undefined, () => 1, NaN]));
{"istasyon":"A1","gecersiz":null,"sonsuz":null}
[null,null,null]

İki farklı davranış var. Nesne özelliklerinde undefined, fonksiyon ve sembol değerleri tümüyle atlanır: anahtar çıktıda hiç görünmez. Dizi elemanlarında aynı değerler null’a çevrilir, çünkü dizinin uzunluğu korunmalıdır.

NaN ve Infinity her iki durumda da null olur. Bu, biçimin bu değerleri hiç tanımamasındandır. Sonuç sessizdir: geçersiz bir ölçüm, ayrıştırıldığında “bilinçli olarak boş” anlamına gelen null değerine dönüşür. İlkel Tipler dersinde kurulan nullundefined ayrımı çevrimden sağ çıkmaz.

İki durumda ise hata verilir:

const a = { istasyon: "A1" };
a.kendisi = a;
try {
  JSON.stringify(a);
} catch (hata) {
  console.log(hata.name);
}
try {
  JSON.stringify({ sayac: 1n });
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
TypeError
TypeError: Do not know how to serialize a BigInt

Döngüsel başvuru — bir nesnenin doğrudan veya dolaylı olarak kendisini içermesi — sonsuz bir metin üretirdi. Büyük tam sayılar ise Büyük Tam Sayılar dersinde tartışıldığı gibi biçimin duyarlık garantisi olmadığı için reddedilir. İlk hatanın iletisi çalışma zamanına göre değiştiği için yalnızca adı yazdırıldı.

Dönüşümü Uyarlamak

Her iki yöntem de bir geri çağrı alabilir. Dizileştirmede geri çağrı her anahtar–değer çifti için çağrılır ve döndürdüğü değer yazılır; undefined döndürmek alanı atlar.

const kayit = {
  istasyon: "A1",
  zamanNs: 1700000000123456789n,
  sicaklik: 21.5,
  gizli: "yazilmasin",
};

const metin = JSON.stringify(kayit, (anahtar, deger) => {
  if (anahtar === "gizli") return undefined;
  if (typeof deger === "bigint") return deger.toString();
  return deger;
});
console.log(metin);

const geri = JSON.parse(metin, (anahtar, deger) =>
  anahtar === "zamanNs" ? BigInt(deger) : deger,
);
console.log(geri.zamanNs === kayit.zamanNs, typeof geri.zamanNs);
{"istasyon":"A1","zamanNs":"1700000000123456789","sicaklik":21.5}
true bigint

Büyük Tam Sayılar dersinde bırakılan sorun burada tamamlandı: değer metin olarak yazıldı, okunurken geri çevrildi ve son basamağına kadar korundu. gizli alanı çıktıya girmedi.

İki geri çağrı simetriktir ve birlikte tasarlanmalıdır: yazan taraf hangi alanı hangi biçime çevirdiyse, okuyan taraf aynı alanı geri çevirmelidir. Aksi halde tip sessizce değişir.

Dizileştirmede geri çağrı yerine bir alan listesi de verilebilir:

const kayit = { istasyon: "A1", sicaklik: 21.5, nem: 48 };
console.log(JSON.stringify(kayit, ["istasyon", "sicaklik"]));
{"istasyon":"A1","sicaklik":21.5}

Sayı Duyarlığının Sınırı

JSON metnindeki sayılar, ayrıştırılırken dilin sayı tipine çevrilir. İlkel Tipler dersindeki güvenli aralık burada da geçerlidir:

console.log(JSON.parse("9007199254740993"));
console.log(JSON.parse("0.1") + JSON.parse("0.2"));
console.log(JSON.stringify(0.1 + 0.2));
9007199254740992
0.30000000000000004
0.30000000000000004

İlk satır sessiz bir kayıptır: metinde doğru yazılmış bir tam sayı, okunduğunda başka bir değere düştü. Biçim bu değeri taşıyabiliyordu; kaybeden ayrıştırıcı değil, hedef tiptir. Kayıpsız aktarım için değer metin olarak yazılır — yukarıdaki geri çağrı kalıbı budur.

Son iki satır kayan noktalı gösterimin çevrimden sağ çıktığını gösteriyor: yazılan metin aynı bit örüntüsüne geri döner.

Ayrıştırma Katıdır

JSON dilbilgisi, JavaScript nesne sözdiziminden dardır:

const bozuklar = ['{"a":1,}', "{'a':1}", "{a:1}", ""];
for (const m of bozuklar) {
  try {
    JSON.parse(m);
    console.log(JSON.stringify(m), "-> gecerli");
  } catch (hata) {
    console.log(JSON.stringify(m), "->", hata.name);
  }
}
"{\"a\":1,}" -> SyntaxError
"{'a':1}" -> SyntaxError
"{a:1}" -> SyntaxError
"" -> SyntaxError

Dördü de JavaScript kaynağında geçerli ya da zararsız yazımlardır; JSON’da değildir. Sondaki virgül, tek tırnak, tırnaksız anahtar ve boş metin reddedilir. Bu katılık, biçimin farklı dillerde aynı şekilde okunmasını sağlar.

Hata adı SyntaxError’dır; ileti metni ayrıştırıcıya göre değişir. Hata Yönetimi dersindeki kural burada uygulanır: dallanma ada göre yapılır.

Ölçüm Betiğine Uygulama

Kursun ekseni artık bir tam çevrim yazabilir: metinden oku, dönüştür, metne yaz.

const gelen = `[
  {"istasyon":"A1","sicaklik":21.5,"nem":48},
  {"istasyon":"A2","sicaklik":19.75,"nem":null},
  {"istasyon":"B1","sicaklik":23,"nem":41}
]`;

const kayitlar = JSON.parse(gelen);
const zenginlestirilmis = kayitlar.map((k) => ({
  ...k,
  sapma: Number((k.sicaklik - 20).toFixed(2)),
  nemVar: k.nem != null,
}));

console.log(JSON.stringify(zenginlestirilmis, null, 2));
[
  {
    "istasyon": "A1",
    "sicaklik": 21.5,
    "nem": 48,
    "sapma": 1.5,
    "nemVar": true
  },
  {
    "istasyon": "A2",
    "sicaklik": 19.75,
    "nem": null,
    "sapma": -0.25,
    "nemVar": false
  },
  {
    "istasyon": "B1",
    "sicaklik": 23,
    "nem": 41,
    "sapma": 3,
    "nemVar": true
  }
]

Üç karar birlikte alındı. Dönüşüm yeni nesneler üretti; Nesneler dersindeki kural gereği girdi değiştirilmedi. Sapma değeri toFixed ile yuvarlanıp Number ile sayıya geri çevrildi — aksi halde JSON’a metin olarak yazılırdı. nem alanının varlığı != null denetimiyle sınandı, böylece null değeri de eksiklik sayıldı.

Ayrıştırılan metnin dışarıdan gelmesi durumunda ayrıştırma bir try/catch içine alınmalıdır; bozuk girdi programı durdurmamalıdır.

Özet

  • JSON.stringify değeri metne, JSON.parse metni yeni bir değere çevirir; çevrim sonucu özgün nesneye eşit değildir.
  • Nesne özelliklerinde undefined, fonksiyon ve sembol atlanır; dizi elemanlarında null’a çevrilir. NaN ve Infinity her durumda null olur.
  • Döngüsel başvuru ve büyük tam sayılar dizileştirmede hata verir.
  • Geri çağrılar desteklenmeyen tipleri kayıpsız aktarmayı sağlar; yazan ve okuyan taraf simetrik yazılır.
  • JSON sayıları dilin sayı tipine çevrilir; güvenli aralığın üstündeki tam sayılar sessizce yuvarlanır.
  • JSON dilbilgisi katıdır: sondaki virgül, tek tırnak ve tırnaksız anahtar reddedilir.

Sonraki Adım

Ölçüm kayıtları hâlâ zamansızdır: hangi okumanın ne zaman alındığı bilinmiyor. Sonraki ders tarih ve zaman gösterimini ele alacak — zaman damgası kavramı, saat dilimi ve yerel saat ayrımı, ayrıştırma tuzakları ve biçimlendirme. Kursun kapanışı da orada yapılacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat