Python'da JSON verisini CSV'ye çevirmek için önce JSON'u json.loads ya da json.load ile Python nesnesine okursun. Kayıtlar sözlük listesi olarak gelir ve sözlük anahtarları CSV sütunlarına karşılık gelir. Sonra csv.DictWriter ile başlık satırını ve kayıtları dosyaya yazarsın. Tek sözlük gelirse onu [veri] biçiminde listeye sararak aynı yolu izlersin.
JSON ve CSV Neden Birebir Karşılık Gelmez?
JSON hiyerarşiktir. Bir nesnenin içinde başka bir nesne ya da liste durabilir, kayıtlar farklı anahtarlar taşıyabilir. CSV ise satır ve sütun tabanlı düz bir metin biçimidir. Her satır aynı sütun düzenine uyar ve her hücre yalnızca bir metin değeri taşır. Bu iki biçim aynı veri modeli değildir. Bu yüzden dönüşümde önce sütunlar belirlenir, sonra kayıtlar yazılır.
Bu farkın somut sonuçları var. İç içe bir alanı olduğu gibi tek hücreye sığdıramazsın. CSV'de her değer metin olarak saklanır, dolayısıyla JSON'daki sayı, true ve null gibi tür bilgileri dosyada ayrışmaz. Sütun listene almadığın alanlar çıktıda yer almaz. Dönüşümün hangi noktada bilgi kaybettirdiğini baştan bilmek gerekir.
Dönüşüm üç adımdan oluşur:
- JSON'u Python nesnesine oku: dosyadan okuyorsan
json.load(dosya nesnesi alır), elindeki bir metinden okuyorsanjson.loadskullanılır. - Sütun adlarını belirle: hangi anahtarlar başlık satırını oluşturacak, karar ver.
- Satırları yaz:
csv.DictWriterile başlığı ve kayıtları dosyaya aktar.
Üç JSON Yapısında Sütunlar Nasıl Belirlenir?

Karşına çıkan JSON genellikle üç biçimden birindedir. Her biri için sütun belirleme yolu ve olası veri kaybı farklıdır.
| JSON yapısı | CSV'de karşılığı | Sütunlar nasıl belirlenir | Dikkat edilecek veri kaybı |
|---|---|---|---|
| Tek sözlük | Listeye sarılınca tek satır | Sözlüğün anahtarları sütun olur | Kayıp yok; yalnızca değerler metne dönüşür |
| Sözlük listesi (ana senaryo) | Her sözlük bir satır | Sabit liste ya da tüm anahtarların birleşimi | Sabit listede olmayan alanlar dışarıda kalır |
| İç içe alanlar | Düzleştirilmiş sütunlar ya da tek hücrede birleştirilmiş liste | İç anahtarlar birleştirilerek yeni sütun adı üretilir | Liste birleştirilince öğeler tek metne dönüşür, yapı kaybolur |
Sözlük listesinde iki yoldan birini seçersin. Sabit sütun listesi: sütunları kendin yazarsın, çıktı öngörülebilir olur, istemediğin alanlar dışarıda kalır. Anahtar birleşimi: tüm kayıtlardaki anahtarları toplarsın, hiçbir alan kaybolmaz. Ancak kayıtlar farklı anahtarlar taşıyorsa bazı hücreler boş kalır.
Python'ın güncel sürümlerinde sözlükler anahtarları ekleme sırasıyla tutar. dict.fromkeys bu özellikten yararlanarak anahtarları ilk görülme sırasıyla, tekrar etmeden toplar:
kayitlar = [
{"ad": "Defter", "fiyat": 45.5},
{"ad": "Kalem", "renk": "mavi"},
{"ad": "Silgi", "fiyat": 10, "renk": "beyaz"},
]
sutunlar = list(dict.fromkeys(anahtar for kayit in kayitlar for anahtar in kayit))
print(sutunlar)
Beklenen çıktı:
['ad', 'fiyat', 'renk']
Aynı işi küme (set) ile yaparsan tekrarlar elenir ama sütun sırası güvence altında olmaz. Dosyayı her çalıştırışta başlık sırası değişebilir, bu yüzden sıralı birikim tercih edilir. Sütunları belirlemek aslında küçük bir algoritma sorusudur: veriyi tara, görülenleri sırayla biriktir, tekrarı ele. Bu tür adım adım çözüm kurma becerini ölçmek istersen dilden bağımsız çalışan Algoritmik Düşünme Testi iyi bir başlangıç olabilir.
Ürün Listesini CSV'ye Çeviren Çalışan Örnek
Örnekte kurgusal ürün verisi kullanıyoruz. Fiyatlar tamamen uydurmadır. JSON metni bir Python metni olarak elimizde olduğu için json.loads ile okuyoruz:
import csv
import json
json_metin = """[
{"urun_adi": "Defter", "kategori": "Kırtasiye", "fiyat": 45.5},
{"urun_adi": "Kalem Seti", "kategori": "Kırtasiye", "fiyat": 32.0},
{"urun_adi": "Sırt Çantası", "kategori": "Çanta", "fiyat": 399.9},
{"urun_adi": "Su Matarası", "kategori": "Aksesuar", "fiyat": 85.0}
]"""
urunler = json.loads(json_metin)
alanlar = ["urun_adi", "kategori", "fiyat"]
with open("urunler.csv", "w", newline="", encoding="utf-8") as dosya:
yazici = csv.DictWriter(dosya, fieldnames=alanlar)
yazici.writeheader()
yazici.writerows(urunler)
Oluşan urunler.csv dosyası şöyle görünür:
urun_adi,kategori,fiyat
Defter,Kırtasiye,45.5
Kalem Seti,Kırtasiye,32.0
Sırt Çantası,Çanta,399.9
Su Matarası,Aksesuar,85.0
Kodu satır satır okuyalım:
json.loads(json_metin)metni Python'da sözlük listesine çevirir.alanlarsütun adlarını ve sırasını belirler. Bu, sabit sütun listesi yoludur.newline=""csv modülünün satır sonlarını kendisinin yönetmesi için açılış parametresidir.encoding="utf-8"ile dosya UTF-8 olarak yazılır. Türkçe karakterlerin Excel'de doğru açılması için gereken ayarları ilerleyen bölümde ayrıca ele alacağız.DictWriterher sözlüğüfieldnamessırasına göre satıra dizer.writeheader()başlık satırını,writerows(urunler)listedeki tüm kayıtları yazar.
JSON bir dosyadaysa metin yerine dosya nesnesini json.load'a verirsin: with open("urunler.json", encoding="utf-8") as f: urunler = json.load(f). Sonrası aynıdır.
Tek sözlük gelirse
writerows bir kayıt listesi bekler. Elinde tek sözlük varsa onu [veri] biçiminde listeye sararsın:
tek = {"urun_adi": "Defter", "kategori": "Kırtasiye", "fiyat": 45.5}
with open("tek_urun.csv", "w", newline="", encoding="utf-8") as dosya:
yazici = csv.DictWriter(dosya, fieldnames=list(tek))
yazici.writeheader()
yazici.writerows([tek])
Beklenen çıktı, başlık ve tek veri satırından oluşur:
urun_adi,kategori,fiyat
Defter,Kırtasiye,45.5
Burada sütun adlarını list(tek) ile sözlüğün kendi anahtarlarından aldık. Bu örnekte bilgi kaybı yok. Yine de 45.5 artık bir sayı olarak değil, dosyada metin olarak duruyor.
Eksik ve Fazla Alanlar: restval ve extrasaction
Gerçek JSON verisinde her kayıt aynı anahtarlara sahip olmayabilir. csv.DictWriter bu iki durumu farklı ele alır. Kayıtta eksik anahtar varsa hücre boş kalır ya da restval ile belirlediğin değer yazılır. Kayıtta fieldnames içinde olmayan fazladan anahtar varsa varsayılan davranış hata vermektir.
Eksik alan: restval
Python'un csv dokümantasyonuna göre restval varsayılan olarak boş metindir. Sözlükte fieldnames içindeki bir anahtar yoksa o hücreye bu değer yazılır.
import csv
urunler = [
{"ad": "Defter", "fiyat": 45.5},
{"ad": "Kalem"}, # fiyat alanı yok
]
with open("urunler.csv", "w", encoding="utf-8", newline="") as f:
yazici = csv.DictWriter(f, fieldnames=["ad", "fiyat"], restval="YOK")
yazici.writeheader()
yazici.writerows(urunler)
Beklenen urunler.csv içeriği:
ad,fiyat
Defter,45.5
Kalem,YOK
restval vermezsen son satır Kalem, olur. Burada önemli bir sınır var: CSV'de boş hücre, "bu alan hiç yoktu" ile "değer boş metindi" ayrımını göstermez. İkisi de aynı görünür. Bu ayrım senin için önemliyse restval ile belirgin bir işaret seçebilirsin, ama gerçek verinde "YOK" metni de geçiyorsa karışıklık yine olabilir.
Fazla anahtar: extrasaction
extrasaction varsayılanı 'raise''dir. Sözlükte fieldnames dışında bir anahtar bulunursa ValueError oluşur.
fazla = [
{"ad": "Defter", "fiyat": 45.5},
{"ad": "Silgi", "fiyat": 10, "stok": 120},
]
with open("urunler.csv", "w", encoding="utf-8", newline="") as f:
yazici = csv.DictWriter(f, fieldnames=["ad", "fiyat"])
yazici.writeheader()
yazici.writerows(fazla)
Hata mesajı şuna benzer: ValueError: dict contains fields not in fieldnames: 'stok'. Hata vermesi aslında bir koruma sağlar; veri yapısında beklemediğin bir değişiklik olduğunu sana haber verir.
extrasaction="ignore" verirsen fazla anahtarlar atlanır:
yazici = csv.DictWriter(f, fieldnames=["ad", "fiyat"], extrasaction="ignore")
Çıktı artık Defter,45.5 ve Silgi,10 satırlarını içerir. Dikkat: stok değeri CSV'ye hiç yazılmaz ve hiçbir uyarı görmezsin. Bu bir veri kaybıdır. Kaybın bilinçli olduğundan emin değilsen ignore kullanma.
Hangi durumda hangi seçenek?
- Eksik alan normalse ve boş hücre yeterliyse: varsayılan
restvalyeterlidir. - Eksikliği görünür kılmak istiyorsan:
restval="YOK"gibi bir işaret seç. - Tüm kayıtlarda aynı anahtarlar bekleniyorsa:
extrasactionvarsayılanı kalsın, hata yakalayıcı olarak işe yarar. - Fazla alanların CSV'de yer almaması bilinçli bir karar ise:
extrasaction="ignore"kullan, kaybı kendin biliyor ol. - Fazla alanı da saklamak istiyorsan:
fieldnameslistesine o anahtarı ekle (tüm kayıtların anahtarlarını önceden tarayarak oluşturabilirsin).
İç İçe JSON Alanları: Düzleştirme ya da Tek Hücrede Birleştirme

CSV'de her hücre tek bir düz değer taşır. Bir kaydın içinde başka bir nesne ya da liste varsa bunu satır-sütun yapısına sığdırmak için bir karar vermen gerekir. Kurgusal bir ürün kaydı üzerinden iki yaklaşımı görelim:
urun = {
"ad": "Kalem",
"fiyat": 12.5,
"tedarikci": {"ad": "Ege Kırtasiye", "sehir": "İzmir"},
"etiketler": ["kalem", "okul"],
}
1. Düzleştirme
İç nesnenin alanlarını tedarikci_ad ve tedarikci_sehir gibi ayrı sütunlara taşırsın. Kayıt başına küçük bir yardımcı fonksiyon yeterlidir:
import csv
def duzlestir(u):
return {
"ad": u["ad"],
"fiyat": u["fiyat"],
"tedarikci_ad": u["tedarikci"]["ad"],
"tedarikci_sehir": u["tedarikci"]["sehir"],
"etiketler": ";".join(u["etiketler"]),
}
satirlar = [duzlestir(urun)]
with open("urunler.csv", "w", encoding="utf-8", newline="") as f:
yazici = csv.DictWriter(f, fieldnames=list(satirlar[0]))
yazici.writeheader()
yazici.writerows(satirlar)
Beklenen çıktı:
ad,fiyat,tedarikci_ad,tedarikci_sehir,etiketler
Kalem,12.5,Ege Kırtasiye,İzmir,kalem;okul
2. Liste değerini tek hücrede birleştirme
Yukarıdaki etiketler sütunu bu yöntemi kullanıyor: ";".join(...) listeyi tek metne çevirir. Bunun açık dezavantajları var:
- Ayırıcı değerin içinde geçerse (ör. bir etiket "kalem;jel" ise) CSV'yi okuyan kişi öğeleri yanlış ayırır.
- Geri dönüşüm otomatik değildir; listeyi geri elde etmek için
split(";")gibi bir adımı sen yazmalısın. - Her şey metne dönüşür. Listedeki sayı, metin ve boş değer ayrımı kaybolur.
- Boş liste ile hiç olmayan alan aynı boş hücreyle görünür.
Düzleştirmek istemezsen iç nesneyi json.dumps ile metne çevirip tek hücreye koyabilirsin; ancak hücre, CSV açısından yine yalnızca bir metindir.
İç içelik çok derinse ya da kayıtlar arasında düzensizse (bir kayıtta üç, diğerinde yedi seviye gibi) CSV doğru araç olmayabilir. Bu durumda verinin JSON olarak kalması çoğu zaman daha dürüst bir seçimdir.
Türkçe Karakterler İçin encoding ve newline Ayarları
CSV yazarken open("dosya.csv", "w", encoding="utf-8", newline="") kalıbını kullan. encoding="utf-8" ş, ğ, ı, İ, ö, ü, ç gibi karakterlerin bozulmadan yazılmasını sağlar. Açıkça vermezsen Python sistemin varsayılan kodlamasını kullanır ve bu makineden makineye değişebilir.
newline="" ise Python'un csv dokümantasyonunda önerilen bir ayardır. Satır sonlarını csv modülü kendisi yönetir; bu ayarı vermezsen bazı sistemlerde satırlar arasında boş satırlar oluşabilir.
import csv
urunler = [{"ad": "Çığlık Işığı", "fiyat": 99.9}, {"ad": "Şeker Ğ", "fiyat": 5}]
with open("tr.csv", "w", encoding="utf-8", newline="") as f:
yazici = csv.DictWriter(f, fieldnames=["ad", "fiyat"])
yazici.writeheader()
yazici.writerows(urunler)
with open("tr.csv", encoding="utf-8", newline="") as f:
for satir in csv.DictReader(f):
print(satir["ad"], satir["fiyat"])
Beklenen çıktı:
Çığlık Işığı 99.9
Şeker Ğ 5
Dosyayı csv.DictReader ile geri okumak, yazdıklarını hızlıca kontrol etmenin kolay yoludur. JSON dosyasını okurken de open("veri.json", encoding="utf-8") ver. json.dump tarafındaki ensure_ascii ayarı okuma için sorun yaratmaz; json.load hem kaçış dizilerini hem doğrudan yazılmış Türkçe karakterleri aynı biçimde çözer.
Excel'de karakterler bozuk görünürse
Excel, BOM içermeyen UTF-8 CSV dosyasını sürüme ve ayara göre farklı açabilir. Dosyayı Excel'de açtığında Türkçe karakterler bozuk görünüyorsa encoding="utf-8-sig" ile yazmayı deneyebilirsin. Bu kodlama dosyanın başına bir BOM işareti ekler. Her durumda gerekli değildir; ayrıca BOM'u tanımayan bazı araçlarda ilk sütun adının başında görünmez bir karakter belirebilir. Dosyayı Excel dışında da kullanacaksan iki sürümü de deneyip sonucu karşılaştır.
Dönüşümü Doğrulama, Sık Hatalar ve Pandas Notu
CSV dosyası oluştu diye dönüşüm doğru sayılmaz. Veri kaybı çoğu zaman hata vermeden sessizce olur. Dosyayı kapatmadan önce şu kontrol listesinden geçebilirsin:
- Sütun listesi (
fieldnames) kayıtlardaki anahtarlarla uyumlu mu? - Eksik alanlar
restvalile bilinçli bir değere mi çevrildi? - Fazla anahtarlar
extrasaction="ignore"ile atıldıysa, bu veri kaybını bilerek mi kabul ettin? - İç içe alanlar düzleştirildi mi, yoksa tek hücrede mi birleştirildi?
- Dosya
encodingvenewline=""ayarlarıyla mı açıldı? - CSV'deki satır sayısı JSON'daki kayıt sayısına eşit mi?
Son maddeyi kısa bir kodla denetleyebilirsin. DictReader başlık satırını saymadığı için karşılaştırma doğrudan yapılır:
import json, csv
with open("urunler.json", encoding="utf-8") as f:
kayitlar = json.load(f)
with open("urunler.csv", encoding="utf-8-sig", newline="") as f:
satirlar = list(csv.DictReader(f))
print(len(kayitlar), len(satirlar))
Beklenen çıktı, JSON'da üç ürün varsa 3 3 olur. İki sayı farklıysa yazma döngüsünü ve filtreleri kontrol et.
Sık karşılaşılan hatalar
- ValueError:
DictWriter,fieldnamesiçinde olmayan bir anahtar bulduğunda varsayılan ayarla bu hatayı verir. Sütunu listeye ekle ya da veri kaybını bilerekextrasaction="ignore"ile kabul et. - json.JSONDecodeError: Dosya geçerli JSON değildir; örneğin sondaki fazladan virgül ya da tek tırnak buna yol açar. Bu sınıf
ValueError'ın alt sınıfıdır, bu yüzdenexcept ValueErrorda onu yakalar. - Liste yerine sözlük gelmesi: Kök nesne tek bir sözlükse
[veri]ile listeye sar. Kayıtlar bir anahtarın altındaysaveri["urunler"]gibi o listeyi seç.
Pandas hakkında kısa not
Büyük ve düzensiz verilerde pandas ile de aynı dönüşümü yapabilirsin. Bu yazı standart kütüphanede kaldı, çünkü sütunların nasıl seçildiğini ve verinin nerede kaybolabileceğini adım adım görmek için önce bu yol önerilir. Mantığı oturttuktan sonra pandas'a geçmek daha rahat olur. Kendi düzeyini ölçmek istersen ücretsiz Python bilgi testini çözebilirsin.
Bir dönüşümü iki kez kontrol etmek, üç satırlık bir veriyle başlayıp yüzlerce kayda geçerken seni sürprizlerden korur.