jq ile JSON Verisini Dönüştürme
Giriş
Büyük JSON dosyalarıyla çalışırken ihtiyaç duyduğun bilgiyi bulmak ve işlemek zor olabilir. Verileri tek tek kopyalayıp toplam almak hem zaman alıcıdır hem de hata riski yüksektir. Linux sistemlerinde bulunan sed, awk ve grep gibi araçlar yararlı olsa da JSON gibi makine tarafından okunabilir veriler için sınırlı kalır.
jq, JSON verisini işlemek için geliştirilmiş güçlü bir komut satırı aracıdır. Shell script’lerde, AI iş akışlarında ve DevOps süreçlerinde veriyi filtrelemek, dönüştürmek ve analiz etmek için kullanılır. Örneğin:
- Bir API’den gelen JSON cevabından belirli bilgileri çekmek,
- Kubernetes çıktılarında belirli değerleri seçmek,
- Veri mühendisliği süreçlerinde JSON temizleme ve dönüştürme işlemleri yapmak mümkündür.
Modern Kullanım Alanları
- AI entegrasyonu: jq, makine öğrenmesi modelleri için JSON verisini filtreleyip dönüştürerek veri ön işleme adımını kolaylaştırır.
- Performans: C ile yazıldığı için çok hızlıdır; gigabaytlarca JSON verisini verimli şekilde işleyebilir.
--streammodu sayesinde büyük veri kümelerinde hafızayı aşırı zorlamadan çalışır.
Bu kılavuzda jq ile bir örnek JSON dosyasını dönüştürmeyi, filtrelerle veriyi yeniden yapılandırmayı ve modern AI ile DevOps iş akışlarına nasıl entegre edileceğini öğreneceksiniz.
Öne Çıkan Noktalar
Bu kılavuzda öğreneceklerin:
- Temel jq İşlemleri: JSON verisini filtreleme, map kullanımı ve dönüştürme teknikleri
- Performans İyileştirme: Çok büyük (5GB+) JSON dosyalarını stream ve bellek dostu yöntemlerle işleme
- AI Entegrasyonu: jq’nun makine öğrenmesi veri ön işleme ve API çıktılarında kullanımı
- Modern İş Akışları: Kubernetes, CI/CD ve mikroservis ortamlarında jq uygulamaları
- İleri Teknikler: İç içe JSON verileri işleme, koşullu filtreler ve hata yönetimi
Ön Gereksinimler
Bu eğitimi tamamlamak için ihtiyacınız olanlar:
- jq: JSON verilerini okumak ve dönüştürmek için kullanılan araç. Çoğu Linux dağıtımının paket depolarında bulunur.
Ubuntu için kurulum:
sudo apt install jq
Adım 1 — İlk jq Komutunu Çalıştırmak
Bu adımda örnek JSON dosyasını oluşturacak ve jq komutuyla test edeceksiniz. jq hem dosyadan hem de pipe üzerinden veri alabilir; burada dosya kullanacağız.
Önce seaCreatures.json isimli bir dosya oluşturun ve açın (örnek olarak nano kullanılıyor):
nano seaCreatures.json
Dosyanın içine aşağıdaki JSON verisini kopyalayın:
[
{ "name": "Rocky", "type": "seal", "clams": 5 },
{ "name": "Coral", "type": "seal", "clams": 3 },
{ "name": "Finny", "type": "whale", "clams": 2 },
{ "name": "Pearl", "type": "seahorse", "clams": 2 }
]
Bu verilerle eğitim boyunca çalışacağız. En sonda, tek satırlık jq komutlarıyla şunların cevabını alabileceksiniz:
- Deniz canlılarının isimlerini tek tek bir listede göreceksiniz.
- Tüm canlıların toplamda kaç midyesi (clams) olduğunu kolayca hesaplayacaksınız.
- Ve en merak edilen kısım: Bu midyelerin kaçı sadece yunuslara ait, onu da bulabileceksiniz.
Yani özetle, jq ile istediğiniz cevabı birkaç saniyede çıkarabileceksiniz.
Dosyayı kaydedip kapatın. ✅
Artık elimizde bir giriş dosyası var, ama tek başına yeterli değil. jq ile çalışırken ayrıca bir filter (filtre) tanımlamanız gerekir. Bu filtre, verinin nasıl dönüştürüleceğini söyler.
En basit filtre ise nokta (.). Buna identity operator denir ve yaptığı şey çok basit: JSON verisini olduğu gibi alır, hiç değiştirmeden çıktıya verir. Yani bir nevi “dokunmadan geçir” filtresi.
Kurulumun doğru çalışıp çalışmadığını test etmek için önce identity operator (.) kullanabilirsiniz.
Eğer hata alırsanız, büyük ihtimalle seaCreatures.json dosyasında geçersiz bir JSON yapısı vardır. Dosyayı kontrol edin.
Şimdi şu komutu çalıştırın:
jq '.' seaCreatures.json
jq’yu dosyalarla kullanırken her zaman önce bir filtre, sonra da giriş dosyasını belirtirsiniz.
Filtrelerin içinde boşluk veya shell tarafından özel anlamı olan karakterler olabileceği için, filtreyi tek tırnak (') içine almak iyi bir pratiktir. Böylece shell onu parametre olarak görür.
Merak etmeyin: jq çalıştırmak dosyanızı değiştirmez, sadece çıktıyı gösterir.
Komutun çıktısı şöyle görünecektir:
Output
[
{
"name": "Rocky",
"type": "seal",
"clams": 5
},
{
"name": "Coral",
"type": "seal",
"clams": 3
},
{
"name": "Finny",
"type": "whale",
"clams": 2
},
{
"name": "Pearl",
"type": "seahorse",
"clams": 2
}
]
jq çıktısını varsayılan olarak pretty print şeklinde verir.
Yani veriyi girintili, satır satır ve mümkünse renkli gösterir. Bu da özellikle başka araçlardan gelen JSON çıktısını incelerken okumayı kolaylaştırır.
Mesela bir API’den curl ile JSON çektiğinizde, çıktıyı jq '.' komutuna pipe ederek daha okunur hale getirebilirsiniz.
Artık jq düzgün çalışıyor. ✅
Giriş dosyanız hazır olduğuna göre, sıradaki adımlarda farklı filtrelerle veriyi dönüştürüp şu üç bilgiyi elde edeceğiz:
- creatures → canlıların isim listesi
- totalClams → toplam midye sayısı
- totalSealClams → yunusların sahip olduğu midyeler
Bir sonraki adımda ilk olarak creatures bilgisini çekeceğiz.
Adım 2 — creatures Değerini Getirmek
Bu adımda, tüm deniz canlılarının isimlerini çıkaracağız.
creatures alanını kullanarak canlıların adlarını liste halinde elde edeceğiz.
Adımın sonunda şu listeyi görmüş olacaksınız:
Output
[
"Rocky",
"Coral",
"Finny",
"Pearl"
]
Bu listeyi elde etmek için canlıların sadece isimlerini alıp, bunları bir diziye dönüştürmemiz gerekiyor.
Bunu yapmak için filtreyi biraz daraltacağız: tüm değerlerden sadece name alanlarını seçeceğiz.
Dizi üzerinde çalıştığımız için jq’ya “elemanlara tek tek bak” dememiz lazım. Bunun için array value iterator olan .[] kullanılır.
Şimdi şu komutu çalıştırın:
jq '.[]' seaCreatures.json
Artık dizideki her eleman ayrı ayrı çıktılanıyor. Yani isimler tek tek ekrana geliyor:
Output
{
"name": "Rocky",
"type": "seal",
"clams": 5
}
{
"name": "Coral",
"type": "seal",
"clams": 3
}
{
"name": "Finny",
"type": "whale",
"clams": 2
}
{
"name": "Pearl",
"type": "seahorse",
"clams": 2
}
Her elemanı olduğu gibi yazdırmak yerine sadece name alanını almak istiyoruz.
Bunun için pipe operator (|) kullanıyoruz. Bu operatör, her çıktıyı sıradaki filtreye gönderir.
Komut satırında find | xargs kullandıysan, aynı mantık burada da geçerli.
Bir JSON objesinin name özelliğine .name yazarak erişebilirsin.
Pipe ile birlikte şu komutu çalıştır:
jq '.[] | .name' seaCreatures.json
Artık sadece name alanları kaldı, diğer tüm özellikler çıktıdan kayboldu:
Output
"Rocky"
"Coral"
"Finny"
"Pearl"
jq varsayılan olarak geçerli JSON çıktısı üretir. Bu yüzden string değerler her zaman çift tırnak (") içinde görünür.
Eğer tırnakları istemiyorsanız, raw output için -r parametresini ekleyin:
jq -r '.[] | .name' seaCreatures.json
```json
The quotation marks have disappeared:
```bash
[secondary_label Output]
Rocky
Coral
Finny
Pearl
Adım 3 — totalClams Değerini Hesaplamak (map ve add ile)
Bu adımda canlıların toplam kaç midyesi (clams) olduğunu bulacağız.
Manuel hesap yapmaya gerek yok; jq ile çok daha hızlı ve hatasız. Sonuç 12 olacak.
1. clams değerlerini çekmek
Önce her canlıya ait clams değerlerini listeleyelim:
jq '.[] | .clams' seaCreatures.json
Output
5
3
2
2
2. Değerleri diziye almak
Toplama yapabilmek için değerleri bir dizi içine koymamız lazım:
jq '[.[] | .clams]' seaCreatures.json
Output
[
5,
3,
2,
2
]
add filtresini uygulamadan önce komutu daha okunur hale getirmek için map fonksiyonunu kullanabiliriz.
map, dizinin her elemanına verilen filtreyi uygular ve sonuçları tek seferde diziye çevirir.
Örneğin:
jq 'map(.name)' '[{"name":"Rocky"},{"name":"Coral"}]'
Filtreyi yeniden yazarak doğrudan map fonksiyonu kullanın ve komutu çalıştırın:
jq 'map(.clams)' seaCreatures.json
Daha önce aldığınız çıktının aynısını alacaksınız:
Output
[
5,
3,
2,
2
]
Artık elimizde bir dizi olduğuna göre bunu add filtresine pipe ederek toplayabiliriz:
jq 'map(.clams) | add' seaCreatures.json
Komutun çıktısı dizinin toplamı olacaktır:
Output
12
Bu filtreyle toplam midye sayısını (12) hesaplamış olduk. Bunu ileride totalClams değerini oluşturmak için kullanacağız.
Şimdiye kadar 3 sorudan 2’sini çözdük. Sadece bir filtre daha yazmamız kaldı, sonra final çıktıyı üretebileceğiz.
Adım 4 — totalSealClams Değerini Hesaplamak (add ile)
Artık toplam midye sayısını biliyoruz. Şimdi sadece yunusların (seals) kaç midyesi olduğunu bulacağız.
Bunu yapmak için, dizideki elemanlardan yalnızca belirli bir koşulu sağlayanları (type = seal) seçip onların clams değerlerini toplayacağız.
Sonuç 4 olacak Bu değer daha sonra totalSealClams alanında kullanılacak.
Bu sefer tüm clams değerlerini toplamak yerine sadece yunusların (seal) sahip olduklarını sayacağız.
Bunun için select fonksiyonunu kullanıyoruz: select(koşul).
- Koşul doğruysa (true) o değer filtrelenip geçirilir.
- Yanlışsa (false) değer yok sayılır.
Bir dizideki her elemana select uygulamak için onu map ile birlikte kullanabilirsiniz.
Böylece koşulu sağlamayan değerler elenir, sadece istediğiniz öğeler kalır.
Bizim durumda sadece type alanı "seal" olanları tutacağız.
Kullanacağımız filtre şu şekilde:
jq 'map(select(.type == "seal").clams) | add' seaCreatures.json
Bu filtre, Rocky (seal) ve Coral (turtle) için eşleşme bulmaz.
Ama Splish ve Splash adlı iki seal için eşleşir.
Output[
{
"name": "Rocky",
"type": "seal",
"clams": 5
},
{
"name": "Coral",
"type": "seal",
"clams": 3
}
]
Bu çıktıda her canlının clams değeri yanında başka bilgiler de var.
Sadece midye sayılarını almak için, map fonksiyonunun içine alan adını ekleyebilirsiniz:
jq 'map(select(.type == "seal").clams)' seaCreatures.json
map fonksiyonu, dizideki her elemana verilen filtreyi uygular.
Bu yüzden select dört kere çalışır: her canlı için bir kez.
Koşulu sağlayan iki yunus için çıktı üretir, diğerlerini atar.
Sonuçta elinizde sadece yunusların clams değerlerini içeren bir dizi olur:
Output
[2, 2]
Şimdi bu dizi değerlerini add filtresine pipe ederek toplayalım:
jq 'map(select(.type == "seal").clams) | add' seaCreatures.json
Bu komutun çıktısı, sadece seal tipindeki canlıların sahip olduğu midyelerin toplamını verir:
Output
4
Tebrikler 🎉
Burada map ve select kullanarak:
- Diziyi dolaştık,
- Sadece koşula uyan (seal) öğeleri seçtik,
- Onların
clamsdeğerlerini aldık ve - Sonucu topladık.
Bu yöntemle totalSealClams değerini hesaplamış olduk.
Bir sonraki adımda bu değeri de final çıktıya ekleyeceğiz. 🚀
Adım 5 — Veriyi Yeni Bir Yapıya Dönüştürmek
Önceki adımlarda filtreler yazarak veriyi parça parça işledik.
Şimdi bu filtreleri birleştirip tek bir JSON çıktısı oluşturacağız.
Bu çıktıda şu soruların cevabını göreceğiz:
- Deniz canlılarının isim listesi nelerdir?
- Canlıların toplamda kaç midyesi vardır?
- Bu midyelerin kaçı yunuslara aittir?
Hatırlatma olarak:
- İsim listesini bulmak için map(.name) kullandık.
- Toplam midye sayısını bulmak için map(.clams) | add filtresini çalıştırdık.
- Yunusların midyelerini hesaplamak için ise map(select(.type == "seal").clams) | add kullandık.
Şimdi bu üç filtreyi tek bir jq komutunda birleştireceğiz.
Yeni bir JSON objesi oluşturup, istediğimiz bilgileri tek bir yapıda göstereceğiz.
Hatırlatma olarak, başlangıç JSON dosyamız şu şekildeydi:
[
{ "name": "Rocky", "type": "seal", "clams": 5 },
{ "name": "Coral", "type": "seal", "clams": 3 },
{ "name": "Finny", "type": "whale", "clams": 2 },
{ "name": "Pearl", "type": "seahorse", "clams": 2 }
]
Filtreleri birleştirdiğimizde elde edeceğimiz dönüştürülmüş JSON çıktısı şöyle olacak:
Final Output
{
"creatures": [
"Rocky"
"Coral"
"Finny"
"Pearl"
],
"totalClams": 12,
"totalSealClams": 4
}
İşte tüm jq komutunun sözdizimini (örnek olması için boş değerlerle) gösteren bir demo:
jq '{ creatures: [], totalClams: 0, totalSealClams: 0 }' seaCreatures.json
Bu filtre ile üç alanı olan bir JSON objesi oluşturuyoruz:
- creatures → canlıların isim listesi
- totalClams → tüm canlıların sahip olduğu midyelerin toplamı
- totalSealClams → sadece yunusların sahip olduğu midyelerin toplamı
Output
{
"creatures": [],
"totalClams": 0,
"totalSealClams": 0
}
Bu JSON yapısı final çıktıya benziyor ama şu an içindeki değerler doğru değil.
Çünkü seaCreatures.json dosyasından alınmadılar, elle yazıldı.
Şimdi bu sabit değerleri, önceki adımlarda oluşturduğumuz filtrelerle değiştirelim:
jq '{ creatures: map(.name), totalClams: map(.clams) | add, totalSealClams: map(select(.type == "seal").clams) | add }' seaCreatures.json
Yukarıdaki filtre jq’ya şunu yaptırıyor:
- creatures → tüm canlıların
namedeğerlerinden oluşan liste - totalClams → tüm canlıların
clamsdeğerlerinin toplamı - totalSealClams → sadece
type = "seal"olanlarınclamstoplamı
Komutu çalıştırdığınızda şu çıktıyı alırsınız:
Output
{
"creatures": [
"Rocky",
"Coral",
"Finny",
"Pearl"
],
"totalClams": 12,
"totalSealClams": 4
}
Sonuç
JSON verisiyle çalışırken jq, sed gibi metin araçlarıyla zor yapılacak dönüşümleri kolayca yapmanızı sağlar.
Bu eğitimde şunları öğrendik:
- select ile veriyi filtrelemek
- map ile dizi elemanlarını dönüştürmek
- add ile sayıları toplamak
- Bu dönüşümleri birleştirerek yeni JSON yapıları oluşturmak
Kısacası jq, JSON verisiyle uğraşırken elinizi ciddi şekilde rahatlatan güçlü bir araçtır. 🚀