Derin iç içe geçmiş bir API yanıtına bakıp "oraya gömülü tek bir alana ihtiyacım var" diye düşündüyseniz — JSONPath aradığınız araçtır. JSON için bir sorgu dilidir, XML için XPath'in nasıl çalıştığına benzer bir mantıkla tasarlanmıştır. Bir yol ifadesi yazarsınız, eşleşen değerleri geri alırsınız. Döngü yok, manuel gezinme yok. Stefan Goessner bunu 2007'de tanıttı ve yıllarca birbiriyle hafif uyumsuz uygulamalar dolaştıktan sonra, 2024'te resmi olarak RFC 9535 olarak standartlaştırıldı. JSONPath ifadelerini JSON Path aracıyla interaktif olarak da keşfedebilirsiniz.

Kullanacağımız Veri Seti

Soyut örnekler yerine gerçekçi bir şeyle çalışalım — bir e-ticaret sipariş yanıtı. Bu, bir sipariş yönetimi API'sinden dönen JSON türüdür:

json
{
  "order": {
    "id": "ORD-9182",
    "status": "shipped",
    "customer": {
      "id": "CUST-441",
      "name": "Maria Chen",
      "email": "[email protected]"
    },
    "shippingAddress": {
      "street": "14 Maple Avenue",
      "city": "Portland",
      "state": "OR",
      "zip": "97201",
      "country": "US"
    },
    "lineItems": [
      {
        "sku": "WH-1042",
        "name": "Wireless Headphones",
        "qty": 1,
        "unitPrice": 89.99,
        "inStock": true
      },
      {
        "sku": "CB-USB-C",
        "name": "USB-C Charging Cable",
        "qty": 2,
        "unitPrice": 12.49,
        "inStock": true
      },
      {
        "sku": "SC-PRO-7",
        "name": "Phone Stand Pro",
        "qty": 1,
        "unitPrice": 34.00,
        "inStock": false
      }
    ],
    "totals": {
      "subtotal": 148.97,
      "shipping": 5.99,
      "tax": 11.92,
      "total": 166.88
    }
  }
}

Kök Operatörü $ ve Temel Gezinme

Her JSONPath ifadesi, belgenin köküne atıfta bulunan $ ile başlar. Oradan noktalar kullanarak gezinirsiniz. Sipariş durumunu öğrenmek mi istiyorsunuz? Bu basit:

text
$.order.status
// → "shipped"

$.order.customer.name
// → "Maria Chen"

$.order.totals.total
// → 166.88

$.order.shippingAddress.city
// → "Portland"

Bir anahtarın boşluk veya özel karakterler içerdiği durumlarda ya da dizi öğelerine dizin ile erişirken faydalı olan köşeli parantez notasyonunu da kullanabilirsiniz. Her iki stil de RFC 9535 sözdizim kurallarına göre geçerlidir:

text
// Dot notation
$.order.customer.email

// Bracket notation — equivalent
$['order']['customer']['email']

// Array index — first line item
$.order.lineItems[0].name
// → "Wireless Headphones"

// Last line item (negative indexing — supported in RFC 9535)
$.order.lineItems[-1].name
// → "Phone Stand Pro"
Negatif indeksleme hakkında: $[-1:] (dilim sözdizimi) ve $.array[-1] (doğrudan negatif indeks) RFC 9535'te geçerlidir, ancak bazı eski uygulamalar bunları desteklemez. Negatif indeks kullanıyorsanız belirli kitaplığınızla test edin.

Wildcards — Tüm Alt Öğeleri Tek Seferde Sorgulama

* wildcard belirli bir düzeydeki tüm öğelerle eşleşir. lineItems[0], lineItems[1] gibi tek tek sormak yerine hepsini tek seferde alabilirsiniz:

text
// All line item names
$.order.lineItems[*].name
// → ["Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]

// All line item unit prices
$.order.lineItems[*].unitPrice
// → [89.99, 12.49, 34.00]

// All line item SKUs
$.order.lineItems[*].sku
// → ["WH-1042", "CB-USB-C", "SC-PRO-7"]

Bu, API yanıtlarındaki dizilerle çalışırken en sık kullanacağınız kalıptır. Uygulama kodunuzda bir dizi üzerinde eşleme yapmak yerine, iş mantığınıza ulaşmadan önce tam olarak ihtiyacınız olan alanı çıkarabilirsiniz.

Özyinelemeli İniş — Her Derinlikte Değer Bulma

.. operatörü, tüm belge ağacında özyinelemeli bir arama yapar. Bulunduğu yerden bağımsız olarak anahtar adıyla eşleşen her düğümü toplayan derinlik öncelikli bir gezinti gibidir:

text
// Find every "name" field anywhere in the document
$..name
// → ["Maria Chen", "Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]

// Find every "city" field anywhere
$..city
// → ["Portland"]

// Find every field called "id" at any depth
$..id
// → ["ORD-9182", "CUST-441"]

$..name'in hem müşteri adını hem de tüm ürün adlarını bulduğunu fark edin — derinliği umursamaz. Bu, şema keşfi için çok güçlüdür: tanıdık olmayan bir JSON parçası aldığınızda ve yapıyı bilmeden belirli bir anahtar için tüm değerleri bulmak istediğinizde. Önce JSON'ın kendisini kontrol etmek istiyorsanız, JSON Doğrulayıcı ve JSON Biçimlendirici kullanışlı başlangıç noktalarıdır.

Dizi Dilimleme

JSONPath, diziler için Python tarzı dilim notasyonunu ödünç alır. Format [start:end:step] şeklindedir; herhangi bir bölüm atlanabilir. Bu, RFC 9535 § 2.3.5'te belgelenmiştir:

text
// First two line items
$.order.lineItems[0:2]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]

// From index 1 to end
$.order.lineItems[1:]
// → [{ sku: "CB-USB-C", ... }, { sku: "SC-PRO-7", ... }]

// Last item only (slice syntax — widely supported)
$.order.lineItems[-1:]
// → [{ sku: "SC-PRO-7", ... }]

// Every other item (step of 2)
$.order.lineItems[0::2]
// → [{ sku: "WH-1042", ... }, { sku: "SC-PRO-7", ... }]

Filtre İfadeleri — Koşula Göre Sorgulama

JSONPath burada gerçek değerini kanıtlar. [?(...)] filtre sözdizimi, dizi öğelerini alan değerlerine göre sorgulamanıza olanak tanır. @ sembolü, test edilen geçerli öğeye atıfta bulunur. Stefan Goessner'ın 2007 tarihli orijinal makalesi bu sözdizimini tanıttı ve RFC 9535 onu resmileştirdi:

text
// Line items under $50
$.order.lineItems[?(@.unitPrice < 50)]
// → [{ sku: "CB-USB-C", unitPrice: 12.49, ... }, { sku: "SC-PRO-7", unitPrice: 34.00, ... }]

// Only items currently in stock
$.order.lineItems[?(@.inStock == true)]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]

// Items where qty is greater than 1
$.order.lineItems[?(@.qty > 1)]
// → [{ sku: "CB-USB-C", qty: 2, ... }]

// Items that are NOT in stock
$.order.lineItems[?(@.inStock == false)].name
// → ["Phone Stand Pro"]

Koşulları da birleştirebilirsiniz. Çoğu uygulama, filtre ifadelerinde && ve ||'u destekler ve [?(@.inStock == true && @.unitPrice < 30)] gibi şeyler yazmanıza olanak tanır. Kitaplığınızın belgelerini kontrol edin — operatör desteği etrafındaki davranış, RFC 9535 standartlaştırmasının düzeltmeyi hedeflediği RFC öncesi uygulamalar arasındaki temel tutarsızlıklardan biriydi.

Alan varlığını kontrol etme: Belirli bir alana sahip öğeleri filtrelemek için (değerinden bağımsız olarak) varlık kontrol sözdizimini kullanın: [?(@.fieldName)]. Örneğin, bazı satır öğelerinde discountCode alanı varsa ve diğerlerinde yoksa, $.order.lineItems[?(@.discountCode)] yalnızca indirimli olanları döndürür.

JavaScript'te JSONPath Kullanımı

JavaScript'te henüz yerleşik JSONPath desteği yok (tarayıcıda document.evaluate() bulunan XPath'in aksine). Bir kitaplığa ihtiyacınız olacak. En popüler ikisi jsonpath ve jsonpath-plus'tır. jsonpath-plus paketi daha aktif olarak sürdürülüyor ve daha iyi RFC 9535 uyumuna sahip:

bash
npm install jsonpath-plus
js
import { JSONPath } from 'jsonpath-plus';

const order = {
  order: {
    id: 'ORD-9182',
    status: 'shipped',
    customer: { name: 'Maria Chen', email: '[email protected]' },
    lineItems: [
      { sku: 'WH-1042', name: 'Wireless Headphones', qty: 1, unitPrice: 89.99, inStock: true },
      { sku: 'CB-USB-C', name: 'USB-C Charging Cable', qty: 2, unitPrice: 12.49, inStock: true },
      { sku: 'SC-PRO-7', name: 'Phone Stand Pro', qty: 1, unitPrice: 34.00, inStock: false }
    ],
    totals: { subtotal: 148.97, shipping: 5.99, tax: 11.92, total: 166.88 }
  }
};

// Get all product names
const names = JSONPath({ path: '$.order.lineItems[*].name', json: order });
console.log(names);
// [ 'Wireless Headphones', 'USB-C Charging Cable', 'Phone Stand Pro' ]

// Get in-stock items under $50
const affordable = JSONPath({ path: '$.order.lineItems[?(@.inStock == true && @.unitPrice < 50)]', json: order });
console.log(affordable.map(item => item.name));
// [ 'USB-C Charging Cable' ]

// Get the order total
const total = JSONPath({ path: '$.order.totals.total', json: order });
console.log(total[0]); // 166.88

JSONPath()'in tek bir değeri sorgularken bile her zaman bir dizi döndürdüğünü unutmayın. Bu tasarım gereğidir: bir yol ifadesi bir seçicidir ve seçici sıfır, bir veya çok düğümle eşleşebilir. Bu nedenle benzersiz bir değeri hedeflediğinizi bildiğinizde her zaman result[0]'ı alın.

Python'da JSONPath Kullanımı

Python'da, jsonpath-ng kitaplığı en kapsamlı seçenektir. Temel spesifikasyonu ve çoğu filtre ifadesi sözdizimini destekler:

bash
pip install jsonpath-ng
python
from jsonpath_ng import parse

order = {
    "order": {
        "id": "ORD-9182",
        "status": "shipped",
        "customer": {"name": "Maria Chen", "email": "[email protected]"},
        "lineItems": [
            {"sku": "WH-1042", "name": "Wireless Headphones", "qty": 1, "unitPrice": 89.99, "inStock": True},
            {"sku": "CB-USB-C", "name": "USB-C Charging Cable", "qty": 2, "unitPrice": 12.49, "inStock": True},
            {"sku": "SC-PRO-7", "name": "Phone Stand Pro", "qty": 1, "unitPrice": 34.00, "inStock": False}
        ],
        "totals": {"subtotal": 148.97, "shipping": 5.99, "tax": 11.92, "total": 166.88}
    }
}

# Parse the expression once, then reuse it (more efficient)
expr = parse("$.order.lineItems[*].name")
names = [match.value for match in expr.find(order)]
print(names)
# ['Wireless Headphones', 'USB-C Charging Cable', 'Phone Stand Pro']

# Get all line items and check stock
expr2 = parse("$.order.lineItems[*]")
for item in [m.value for m in expr2.find(order)]:
    status = "in stock" if item["inStock"] else "OUT OF STOCK"
    print(f"{item['name']} (${item['unitPrice']}) — {status}")

# Output:
# Wireless Headphones ($89.99) — in stock
# USB-C Charging Cable ($12.49) — in stock
# Phone Stand Pro ($34.0) — OUT OF STOCK

parse() + find() kalıbı üretim kullanımı için doğru yaklaşımdır — ifadeyi bir kez derleyin ve yol dizesini her seferinde yeniden ayrıştırmak yerine birçok belge üzerinde çalıştırın. GitHub'daki jsonpath-ng belgeleri genişletilmiş filtre sözdizimi ve eşleşen düğümleri değiştirmek için update() yöntemi hakkında daha fazla ayrıntı içerir.

JSONPath vs jq — Hangisini Seçmeli?

İnsanlar bazen JSONPath ile jq'yu karıştırır — ikisi de "JSON sorgular", ancak farklı sorunları çözerler. İşte pratik karşılaştırma:

  • JSONPath, uygulamalara gömülmek üzere tasarlanmış bir yol ifadesi dilidir. Değerleri sorgular ve çıkarır. JavaScript, Python, Java kodunda veya Kubernetes seçicileri gibi yapılandırma dosyalarında kullanırsınız.
  • jq, kendi programlama diline sahip tam bir komut satırı işlemcisidir. Sorgulayabilir, dönüştürebilir, yeniden şekillendirebilir, filtreleyebilir ve yeni değerler hesaplayabilir. Shell betikleri ve bir terminalde tek seferlik veri işleme için doğru araçtır.
  • JSONPath'i kullanın JSON'dan alanları programatik olarak çıkarması gereken uygulama kodu yazarken — özellikle kitaplığın gömülebilir ve hafif olması gerektiğinde.
  • jq'yu kullanın komut satırındayken, shell betikleri yazarken, API yanıtlarını hata ayıklarken veya basit çıkarmayı aşan dönüşümler (alanları yeniden adlandırma, toplama, yeni yapılar oluşturma) yapmanız gerektiğinde.
  • jq sözdizimi daha güçlü ama aynı zamanda daha karmaşıktır — jq '.order.lineItems[] | select(.inStock) | .name' vs JSONPath'in $.order.lineItems[?(@.inStock)].name'i. Saf çıkarma için JSONPath genellikle daha okunabilirdir.

Bir orta yol da var: zaten JavaScript'te çalışıyorsanız ve yalnızca ara sıra dönüşümlere ihtiyacınız varsa, Lodash'ın _.get() gibi bir şey herhangi bir bağımlılık olmaksızın basit yol erişimini kapsar. Ancak wildcard'lar, özyineleme veya filtre ifadeleri içeren her şey için uygun bir JSONPath kitaplığı buna değer.

Özet

JSONPath, "bir for döngüsünde bu nesneyi elle gezip" ile "bir jq süreci başlatmak" arasındaki gerçek bir boşluğu doldurur. $, ., [*], .. ve [?(filter)] ile rahatladığınızda, sürekli ona başvurduğunuzu göreceksiniz — özellikle yalnızca birkaç alana ihtiyaç duyduğunuz büyük API yükleri üzerinde çalışırken. RFC 9535 standartlaştırması, sözdiziminin artık kararlı olduğu anlamına gelir; bu nedenle bugün yazdığınız ifadeler uyumlu kitaplıklar arasında tutarlı çalışmalıdır. İfadelerinizi JSON Path aracıyla deneyin — JSON'ınızı yapıştırın, bir yol yazın ve sonuçları anında görün. JSON'ınızın önce temizlenmesi gerekiyorsa, JSON Biçimlendirici ve JSON Doğrulayıcı da orada.