Geonode logo
Geonode Team

Geonode Team

Güncellenme: 7 Ekim 2026

Yayınlanma: 2 Eylül 2026

JavaScript’te JSON.parse(): Kapsamlı Bir Kılavuz

`JSON.parse()` bir dizgiyi JavaScript değerine dönüştürür. API’nin tamamı budur ve öğrenmesi yaklaşık on saniye sürer. İlginç olan kısımlar ise bunun etrafındaki her şeydir: reviver işlevi, üretim ortamına sessizce aktarılan sayı hassasiyeti kaybı, `__proto__` özel durumu ve nihayet orijinal kaynak metni görmenizi sağlayan son standart eklenmesi. Bu kılavuz, sadece sorunsuz senaryoları değil, hata durumlarını da ele alarak tüm bunları kapsamaktadır.

Bir proxy şirketinin neden JSON.parse hakkında yazdığına dair bir açıklama. Biz Geonode adresindeyiz ve proxy satıyoruz; müşterilerimizin bildirdiği en yaygın SyntaxError hatası, JSON ile hiçbir ilgisi yok — bu hata Unexpected token '<' şeklindedir; bu da yanıtın JSON yerine HTML olduğu anlamına gelir; yani API bir engellenmiş sayfa, oturum açma yönlendirmesi veya hata sayfası döndürmüştür. İşte dürüst bir uyarı: JSON.parse, aldığınız verilerle ilgili bir hata veriyorsa, başka herhangi bir değişiklik yapmadan önce ham yanıt gövdesini günlüğe kaydedin. On vakadan dokuzunda, ayrıştırıcı mükemmel çalışıyor ve size bir web sayfası gönderildiğini doğru bir şekilde bildiriyor. Proxy satın almak, yalnızca engellendiğiniz belirli durumlarda bu sorunu çözer; yanlış bir URL, süresi dolmuş bir token veya uymanız gereken bir hız sınırı durumunda hiçbir işe yaramaz. Önce yanıtı yazdırın.

Bunu da hallettikten sonra, asıl API'ye geçelim.

Temel Bilgiler ve Gerçekten Karşılaşacağınız Hatalar

const data = JSON.parse('{"name": "Ada", "born": 1815}');
// { name: "Ada", born: 1815 }

İki parametre: metin ve isteğe bağlı bir reviver işlevi. Hepsi bu kadar.

Giriş, JSON dil kurallarına uymadığında SyntaxError

hatası verir; JSON dil kuralları, kullanıcıları yanıltabilecek bazı yönlerden JavaScript nesne literal sözdiziminden daha katıdır. Neredeyse tüm hatalar dört durumdan kaynaklanır.

Tek tırnak işaretleri. MDN bu konuda nettir: "JSON dizeleri çift (tek değil) tırnak işaretleriyle sınırlandırılmalıdır." Geçerli JavaScript, geçersiz JSON.

JSON.parse("{'name': 'Ada'}");  // SyntaxError
JSON.parse('{"name": "Ada"}');  // fine

Sonunda virgül. Modern JavaScript'te geçerli, JSON'da geçersiz:

JSON.parse("[1, 2, 3, 4, ]");  // SyntaxError

Tırnak işaretleri olmadan yazılmış anahtarlar. {name: "Ada"}

Bu, geçerli bir nesne literalidir, ancak JSON değildir. Anahtarlar tırnak içine alınmış dizeler olmalıdır.

Yanıt JSON değildi. Yukarıda açıklanan durum. Unexpected token '<'

, gövdenin <

ile başladığını, yani HTML olduğunu gösterir. Unexpected end of JSON input

genellikle boş bir gövde anlamına gelir — bir 204, kesilmiş bir yanıt veya beklemeyi unuttuğunuz bir fetch.

Her zaman bunu sarmalayın ve gerçekte ne aldığınızı günlüğe kaydedin:

function parseOrThrow(text, url) {
  try {
    return JSON.parse(text);
  } catch (err) {
    throw new Error(
      `Failed to parse JSON from ${url}: ${err.message}. ` +
      `First 200 chars: ${text.slice(0, 200)}`
    );
  }
}

text.slice(0, 200)

kısmı önemlidir. Çıplak bir SyntaxError

, ayrıştırmanın başarısız olduğunu gösterir; ilk 200 karakter nedenini belirtir ve genellikle cevap hemen görülebilir.

Reviver İşlevi ve Amacı

İkinci argüman, değerler ayrıştırılırken bunları dönüştürür:

const data = JSON.parse(text, (key, value) => {
  if (key === "created") return new Date(value);
  return value;
});

Kesin olarak bilinmesi gereken üç davranış.

Derinlik öncelikli olarak çalışır. İç içe geçmiş özellikler, üst öğelerinden önce işlenir ve son çağrı, kök değer için anahtar olarak boş bir dize kullanır. Dolayısıyla, reviver'ınız bir nesneyi gördüğünde, onun alt öğeleri çoktan işlenmiş olur.

**undefined

'i döndürmek, özelliği siler.** MDN: "reviver

işlevi undefined

döndürürse (veya hiçbir değer döndürmezse), özellik nesneden silinir." Bu durum yanlışlıkla kolayca tetiklenebilir — sonuna ulaşamayan bir koşullu dallanma içeren bir reviver, ``undefined`

değerini döndürür ve anahtarları sessizce kaldırır. Varsayılan olarak her zaman açıkça ``value

` değerini döndürün.

Kök değer tamamen değiştirilebilir. "reviver

işlevinden başka bir değer döndürürseniz, bu değer başlangıçta ayrıştırılan değeri tamamen değiştirir. Bu, kök değer için bile geçerlidir."

JSON'da tarih türü bulunmadığından, klasik kullanım alanı tarih canlandırmadır:

const ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;

const data = JSON.parse(text, (key, value) =>
  typeof value === "string" && ISO_DATE.test(value)
    ? new Date(value)
    : value
);

Desen eşleştirmede dikkatli olun. Tarih biçimine sahip her şeyi dönüştüren bir canlandırıcı, dizgi olarak tutmak istediğiniz dizgileri de dönüştürecektir — sürüm numaraları, tanımlayıcılar, zaman damgası gibi görünen kullanıcı içeriği gibi. Şemayı bildiğiniz durumlarda anahtar adına göre eşleştirmeyi tercih edin.

Ayrıca maliyeti de göz önünde bulundurun: canlandırıcı, belgedeki her değer için bir kez çağrılır. Büyük yüklerde bu, performans açısından dikkate alınması gereken önemli bir husustur ve genellikle düz bir şekilde ayrıştırıp daha sonra yalnızca ilgilendiğiniz alanları dönüştürmek daha verimlidir.

Kaynak Metin Erişimi: context.source ve JSON.rawJSON

Bu, son dönemde yapılan en önemli eklemedir ve pek çok geliştirici henüz bununla karşılaşmamıştır.

JSON.parse kaynak metin erişimi önerisi, TC39 sürecinin 4. aşamasına ulaştı; bu, standarda kabul edildiği anlamına geliyor. Bu öneri, doğrudan şu sorunu çözüyor: "ECMAScript değerleri ile JSON metni arasındaki dönüştürme, kayıplı bir işlemdir."

Reviver artık temel değerler için üçüncü bir argüman almaktadır. MDN, context.source

adresini "Bu değeri temsil eden orijinal JSON dizesi" olarak tanımlamaktadır — ve öneri daha kesin bir tanım sunarak, bunu "noktalama işaretlerini içeren ancak baştaki/sonundaki önemsiz boşlukları içermeyen" kaynak metin olarak adlandırmaktadır; buna ek olarak index

, input

ve keys

adresleri de belirtilmiştir.

Bunun neden önemli olduğu, şu örnekle açıkça anlaşılır:

const text = '{"id": 9007199254740993}';

JSON.parse(text).id;
// 9007199254740992  — wrong, silently

JSON.parse(text, (key, value, context) =>
  key === "id" ? BigInt(context.source) : value
).id;
// 9007199254740993n  — correct

Kaynağa erişim olmadan, reviver'ınız bu sayıyı gördüğünde sayı çoktan bir JavaScript double'ına dönüştürülmüş olur. Siz müdahale edemeden hassasiyet kaybolur. context.source

size orijinal rakamları verir.

Öneri ayrıca JSON.rawJSON()

öğesini de ekler; bu, JSON.stringify

tarafından değiştirilmeden gönderilen ham JSON metnini sağlamanıza olanak tanır — böylece döngü tamamlanır ve JSON'dan okunan bir BigInt, bozulma olmadan geri yazılabilir.

JSON.stringify({ id: JSON.rawJSON("9007199254740993") });
// '{"id":9007199254740993}'

Kullanmadan önce hedef ortamlarınızın desteğini kontrol edin, ancak bu artık büyük sayı sorununa bir geçici çözüm değil, doğru cevaptır.

Sayı Hassasiyeti: Yayınlayacağınız Hata

Sessizce hata verdiği ve belirtilerin nedeninden çok uzak bir yerde ortaya çıktığı için ayrı bir başlıkta ele alınmayı hak ediyor.

JSON sayıları, IEEE 754 çift hassasiyetli sayılar olan JavaScript sayılarına dönüştürülür. Number.MAX_SAFE_INTEGER — 9.007.199.254.740.991 — üzerindeki tamsayıların tümü tam olarak temsil edilemez. MDN bunu açıkça ifade eder: sayılar "bu süreçte hassasiyet kaybedebilir".

Bunun tehlikeli yanı, herhangi bir hata atılmamasıdır. Bir sayı alırsınız. Ancak bu, gönderilen sayı değildir.

JSON.parse('{"id": 12345678901234567890}').id;
// 12345678901234567000

Pratikte bu sorunun ortaya çıktığı durumlar:

  • Veritabanı tanımlayıcıları. 64 bitlik tamsayı birincil anahtarlar, güvenli aralığı aşar. İki farklı kayıt, aynı JavaScript sayısına dönüştürülebilir.
  • Snowflake tarzı kimlikler. Birçok büyük platform tarafından kullanılır ve genellikle sınırın üzerindedir.
  • Küçük birimlerdeki finansal tutarlar. Sent veya satoshi cinsinden büyük meblağlar.
  • Nanosaniye cinsinden zaman damgaları. 1970'ten bu yana herhangi bir nanosaniye epoch değeri zaten güvenli aralığın dışındadır.

Tercih sırasına göre üç çözüm yolu:

Dizgi olarak isteyin. API'yi kontrol ediyorsanız, büyük tanımlayıcıları dizgi olarak serileştirin. Bu en temiz çözümdür, her yerde çalışır ve hiçbir maliyeti yoktur. MDN tam olarak şunu önerir: "Büyük sayıları hassasiyet kaybı olmadan aktarmanın bir yolu, bunları dize olarak serileştirmek ve daha sonra BigInt'lere geri dönüştürmektir."

context.source'i bir geri dönüştürücü ile kullanın. Yukarıda belirtildiği gibi, üreticiyi kontrol edemediğiniz ve ortamınız bunu desteklediğinde.

BigInt desteğine sahip bir JSON kütüphanesi kullanın. Eski ortamlar için bunu destekleyen birkaç ayrıştırıcı mevcuttur. Bu, bir bağımlılık ve biraz performans kaybı gerektirir.

İşe yaramayan yöntem: ayrıştırma işleminden sonra sayının "doğru görünüp görünmediğini" kontrol etmek. O aşamada bilgiler kaybolmuş olur ve yanlış bir değer ile doğru bir değer arasında ayrım yapılamaz.

__proto__ ve Prototip Kirliliği

MDN, JSON ile JavaScript’in anlam bakımından farklılık gösterdiği tek durumu şöyle belirtir: “Bir JSON metninin, aynı JavaScript ifadesinden farklı bir değer temsil ettiği tek durum, \"__proto__\" anahtarı ile ilgili olduğunda ortaya çıkar.”

Bir JavaScript nesne literalinde, __proto__ prototipi ayarlar. JSON.parse içinde ise sıradan bir kendi özelliği oluşturur:

const fromLiteral = { __proto__: { admin: true } };
fromLiteral.admin;             // true — prototype was set

const fromJson = JSON.parse('{"__proto__": {"admin": true}}');
fromJson.admin;                // undefined — plain own property
Object.hasOwn(fromJson, "__proto__");  // true

Dolayısıyla JSON.parse burada güvenli bir işlemdir — bu, kasıtlı ve doğru bir davranıştır.

Tehlike, bundan sonra olanlarda yatmaktadır. Prototip kirliliği güvenlik açıkları, neredeyse her zaman, tehlikeli anahtarları filtrelemeyen kodlar tarafından ayrıştırılan verilerin başka bir nesneye birleştirilmesi durumunda ortaya çıkar:

// Unsafe: a naive deep merge can walk into Object.prototype
function merge(target, source) {
  for (const key in source) {
    if (typeof source[key] === "object") {
      merge(target[key] ?? (target[key] = {}), source[key]);
    } else {
      target[key] = source[key];
    }
  }
}

__proto__ içeren bir yük beslerseniz, programın tamamı için Object.prototype adresini değiştirebilirsiniz. Savunma yöntemleri:

Tehlikeli anahtarları açıkça filtreleyin — __proto__, constructor, prototype — güvenilmeyen verilere dokunan her türlü birleştirme veya atamada.

Object.create(null) kullanın; böylece kirlenebilecek bir prototip kalmaz.

Map kullanın; yapılandırılmış bir nesne yerine gerçekten bir anahtar-değer deposu oluşturuyorsanız.

Bir şemaya göre doğrulayın. Genel cevap budur ve diğer sorunları da yakalar. Ayrıştırma ve doğrulama ayrı adımlardır ve her ikisi de gereklidir.

JSON.parse ile eval ve Response.json() Karşılaştırması

eval'i asla kullanmayın. Bu işlev rastgele kod çalıştırır, bu amaç için daha yavaştır ve JSON olmayan verileri kabul eder. eval'in JSON'u ayrıştırmak için doğru araç olduğu hiçbir durum yoktur.

Response.json(), fetch ile çalışırken ihtiyacınız olan işlevdir. Bu işlev, gövdeyi okur ve tek adımda ayrıştırır:

const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();

res.ok kontrolü, kullanıcıların genellikle atladığı bir kısımdır ve bunu atlamak, giriş bölümünde bahsedilen Unexpected token '<' hatasının doğrudan nedenidir. fetch, HTTP hata durumlarını reddetmez — 403 veya 500 hataları normal şekilde çözülür ve ardından .json() bir hata sayfasını ayrıştırmaya çalışır. Önce durumu kontrol edin ve titiz davranmak istiyorsanız content-type adresini de kontrol edin:

const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status} from ${url}`);
const type = res.headers.get("content-type") ?? "";
if (!type.includes("application/json")) {
  const body = await res.text();
  throw new Error(`Expected JSON, got ${type}: ${body.slice(0, 200)}`);
}
const data = await res.json();

Response.json()'un reviver kabul etmediğini unutmayın. Eğer bir reviver'a ihtiyacınız varsa, res.text() adresini ve ardından JSON.parse adresini kullanın.

Kütüphaneler bu konuda da farklılık gösterir — bazıları otomatik olarak ayrıştırma yapar ve 2xx dışındaki durum kodlarında hata atar; bu da hata işleme işleminin nereye ait olacağını değiştirir. Bu davranışı axios ile fetch karşılaştırmasında inceledik.

Sayfayı Donmadan Büyük JSON Dosyalarını Ayrıştırma

JSON.parse işlemi eşzamanlı ve engelleyicidir. Ana iş parçacığında büyük bir belgenin ayrıştırılması, işlem süresince arayüzü dondurur — bu, akış bozukluğunun yaygın ve kolayca teşhis edilebilen bir nedenidir.

Genel kılavuz: Bir megabaytın altındaysa, bu konuyu dert etmeyin. Bir ile on arasında ise, en yavaş hedef cihazınızda ölçüm yapın. Onun üzerindeyse, başka bir yol deneyin.

Seçenekler, çaba gerektirme derecesine göre artan sırayla:

Web Worker’a taşıyın. En basit ve gerçek çözüm budur. Ana iş parçacığı dışında ayrıştırma işlemini gerçekleştirin ve sonucu geri gönderin. Sonucun aktarılmasının kendi yapılandırılmış klonlama maliyeti olduğunu unutmayın; bu nedenle, işçi sonraki işlemleri de gerçekleştirdiğinde bu yöntem en çok fayda sağlar.

Daha az veri isteyin. Sayfalandırma, alan seçimi, daha dar bir uç nokta. Neredeyse her zaman doğru cevaptır, ancak API’nin sahibi ile görüşmeyi gerektirdiği için neredeyse her zaman göz ardı edilir.

Akış tabanlı bir ayrıştırıcı kullanın. Ağaç yapısını tamamen oluşturmak yerine, değerleri geldikçe yayınlayan kütüphaneler mevcuttur. Belgeler gerçekten büyükse veya yükün sadece bir kısmına ihtiyacınız varsa bu yöntem faydalıdır.

Satır sonu ile ayrılmış JSON kullanın. Büyük koleksiyonlar için, her satır bağımsız olarak ayrıştırıldığından ve kesik bir akış bile tam kayıtlar sağladığından, satır başına bir JSON belgesinin aşamalı olarak işlenmesi çok daha kolaydır. Biçimi siz kontrol ediyorsanız, bu genellikle daha iyi bir tasarımdır — diğer biçimlerle karşılaştırıldığında ortaya çıkan artılar ve eksiler, JSON ve CSV karşılaştırmamızda ele alınmıştır.

JSON.parse'in Yanlış Araç Olduğu Durumlar

Giriş verisi JSON olmadığında. JSON5, JSONC ve yorumlar ile sondaki virgül içeren yapılandırma dosyalarının hepsi kendi ayrıştırıcılarına ihtiyaç duyar. JSON.parse bunları doğru bir şekilde reddedecektir ve yorumları düzenli ifadeyle kaldırmaya çalışmamalısınız — bu yol, yazmak istemediğiniz bir ayrıştırıcıya yol açar.

Sadece ayrıştırma değil, doğrulama da gerektiğinde. Başarılı bir ayrıştırma, sözdiziminin geçerli olduğunu gösterir. Ancak gerekli alanların mevcut olup olmadığı veya doğru türlere sahip olup olmadığı hakkında hiçbir bilgi vermez. Önce ayrıştırın, sonra doğrulayın; şema doğrulama kütüphanesi ikinci adım için doğru araçtır, try/catch ise değildir.

Verilerin kayıpsız bir şekilde gidip gelmesi gerektiğinde. Büyük tamsayılar, tarihler, undefined, fonksiyonlar, Map, Set, NaN, Infinity — bunların hiçbiri JSON'da bozulmadan kalmaz. Kayıpsız gidip gelme bir gereklilikse, ya context.source ve JSON.rawJSON adreslerini bilinçli bir şekilde kullanın ya da bunun için tasarlanmış bir format kullanın.

Her render işleminde ayrıştırma yapıyorsanız. Yoğun bir yolda aynı dizeyi tekrar tekrar ayrıştırmak tamamen israftır. Bir kez ayrıştırın ve önbelleğe alın.

Dize, kontrol etmediğiniz bir veri alımından geliyorsa. Yazının başında bahsettiğimiz durum; en yaygın olanı olduğu için tekrar belirtelim. Alınan verilerde JSON.parse hatası veriyorsa, hata kaynak kodda (upstream) demektir. Durum kodunu kontrol edin, içerik türünü kontrol edin, gövdeyi günlüğe kaydedin. Ayrıştırıcı size gerçeği söylüyor.

Sık Sorulan Sorular

JSON.parse ne işe yarar?

JSON biçimindeki bir dizeyi bir JavaScript değerine (nesne, dizi, dize, sayı, boole veya null) dönüştürür. Ayrıştırma işlemi sırasında her bir değeri dönüştürebilen, isteğe bağlı bir reviver işlevi alır. Giriş geçerli bir JSON değilse, SyntaxError hatası verir.

JSON.parse neden "Beklenmeyen belirteç '<'" hatası veriyor?

Çünkü dize < ile başlıyor; bu da JSON yerine HTML aldığınız anlamına gelir — genellikle bir hata sayfası, oturum açma yönlendirmesi veya engelleme sayfasıdır. Ayrıştırıcı doğrudur; sorun istek tarafındadır. Yanıt gövdesinin ilk 200 karakterini günlüğe kaydedin; nedeni genellikle açıkça görülür.

JavaScript'te büyük sayılar içeren JSON'u nasıl ayrıştırabilirim?

Değer reviver'a ulaştığında hassasiyetini zaten kaybetmiş olacağından, reviver'ın context.source argümanını kullanarak orijinal basamakları okuyun ve bir BigInt oluşturun. Daha da iyisi, API'yi kontrol ediyorsanız, büyük tanımlayıcıları dize olarak serileştirin.

JSON.parse'deki reviver işlevi nedir?

Her anahtar-değer çifti için, derinlik öncelikli olarak çağrılan ve boş bir dize anahtarı altındaki kök ile biten isteğe bağlı ikinci bir argümandır. Döndürdüğü değer, mevcut değeri değiştirir; undefined döndürülürse özellik silinir. Genellikle dizeleri Date gibi daha zengin türlere dönüştürmek için kullanılır.

JSON.parse güvenli midir?

Kod yürütme açısından evet — eval'dan farklı olarak, hiçbir zaman bir şey yürütmez. Ayrıca __proto__'i güvenli bir şekilde işler; prototipi ayarlamak yerine düz bir kendi özelliği oluşturur. Risk, daha sonra yaptıklarınızda yatmaktadır: Güvenilmeyen ayrıştırılmış verileri, __proto__ ve constructor'i filtrelemeden diğer nesnelere birleştirmek, prototip kirliliğine yol açar.

JSON.parse ile Response.json() arasındaki fark nedir?

Response.json(), bir fetch yanıt gövdesini okur ve tek adımda ayrıştırır; ayrıca bir reviver kabul etmez. JSON.parse ise elinizde bulunan bir dizgi üzerinde çalışır. fetch'ın HTTP hatalarında işlemeyi durdurmadığını unutmayın; bu nedenle .json()'i çağırmadan önce res.ok adresini kontrol edin, aksi takdirde bir hata sayfasını ayrıştırmış olursunuz.

JSON.parse, yorumları veya sondaki virgülleri işleyebilir mi?

Hayır. Her ikisi de geçersiz JSON'dur ve her ikisi de SyntaxError hatası verir. Girişinizde bunlar varsa, bu JSON5 veya JSONC'dir ve bunları ayıklayan bir düzenli ifade yerine bu biçime uygun bir ayrıştırıcıya ihtiyaç duyar.

JSON.parse ana iş parçacığını bloke eder mi?

Evet, senkron bir işlemdir. Bir megabaytın altındaki belgeler için bu önemsizdir; ancak büyük verilerde gözle görülür bir donmaya neden olur. Ayrıştırma işlemini bir Web Worker'a taşıyın, daha az veri isteyin veya akış tabanlı bir ayrıştırıcı kullanın.

Sonuç Olarak

JSON.parse, iki parametreli bir imza yapısına sahiptir ve arkasında şaşırtıcı derecede derin bir yapı barındırır. Buradan çıkarılması gereken dersler, gürültüyle değil, sessizce hata veren kısımlardır.

Sayı hassasiyeti en ciddi sorundur: büyük tamsayılar sessizce bozulur, hiçbir hata atılmaz ve aşağı akışta bir şey bozulana kadar yanlış değer doğru değerden ayırt edilemez. Çözüm, ya kaynakta dize kodlu tanımlayıcılar kullanmak ya da reviver'ın context.source argümanını kullanmaktır; bu argüman şu anda 4. aşamadadır ve standardın bir parçasıdır.

Reviver, özellikle tarihler için hak ettiğinden daha az kullanılıyor; ancak varsayılan yolda value döndürmeyi unutmak, özelliklerin sessizce silinmesine neden olur. Prototip kirliliği ise JSON.parse için bir sorun değildir — işlev __proto__ adresini doğru şekilde işler — ancak sonucu birleştiren her şey için bir sorundur ve bu da önemli sayılabilecek kadar yakındır.

Geri kalan her şey tek bir alışkanlığa indirgenir: aldığınız verilerde ayrıştırma başarısız olduğunda, herhangi bir kod değişikliği yapmadan önce ham gövdeyi günlüğe kaydedin. Hata mesajı neredeyse her zaman cevabı içerir ve genellikle sorun, en başından beri JSON almamış olmanızdır.