JSONPath Nedir?
JSONPath, JSON belgelerinden değer çıkarmak için kullanılan bir sorgu dilidir. Stefan Goessner tarafından 2007 yılında XML için XPath'e karşılık gelecek şekilde önerilen JSONPath, döngü yazmadan veya elle dolaşım kodu oluşturmadan bir kitap dizisindeki tüm yazar alanlarını seçmek için $.store.book[*].author gibi ifadeler yazmanızı sağlar. Dil, Şubat 2024'te IETF tarafından RFC 9535 olarak standartlaştırıldı.
Bir JSONPath ifadesi her zaman belgenin kökünü temsil eden $ ile başlar. Buradan itibaren seçiciler zinciri oluşturulur: nesne anahtarları için nokta gösterimi, dizi indisleri için köşeli parantez gösterimi, tüm alt öğeler için joker karakterler ve iç içe geçmiş her düzeyde arama yapmak için özyinelemeli iniş operatörü (..). [?(@.price < 10)] gibi filtre ifadeleri, değerlerine karşı değerlendirilen koşullara göre öğe seçmenizi sağlar.
JSONPath; Postman gibi API test araçlarında, Datadog ve Splunk gibi gözlemlenebilirlik platformlarında, Apache NiFi gibi iş akışı motorlarında ve JavaScript, Python, Go, Java ile C# genelindeki programlama kütüphanelerinde kullanılır. İfadelerinizi koda veya yapılandırma dosyalarına yerleştirmeden önce gerçek verilerle test etmek, JSON yapısının varsayımlarınızla eşleşmediği durumlardaki sessiz hataları önler.
Bu JSONPath Test Aracını Neden Kullanmalısınız?
JSONPath ifadelerini elle yazmak hataya açıktır. Eksik bir nokta, yanlış parantez türü veya hatalı filtre söz dizimi, hiçbir hata mesajı vermeden boş sonuç döndürebilir. Bu araç, ifadenizin neyle eşleştiğine dair anlık görsel geri bildirim sağlar.
JSONPath Kullanım Senaryoları
JSONPath Söz Dizimi Başvurusu
RFC 9535, standart JSONPath söz dizimini tanımlar. Aşağıdaki tablo, çoğu sorguda kullanacağınız operatörleri kapsar. Tüm ifadeler $ (kök düğüm) ile başlar ve belge yapısında gezinmek için bir veya daha fazla seçici zinciri oluşturur.
| Operatör | Açıklama | Örnek |
|---|---|---|
| $ | Root element | $.store |
| .key | Child property | $.store.book |
| [n] | Array index (zero-based) | $.store.book[0] |
| [*] | All elements in array/object | $.store.book[*] |
| .. | Recursive descent | $..author |
| [start:end] | Array slice | $.store.book[0:2] |
| [?()] | Filter expression | $.store.book[?(@.price<10)] |
| @ | Current node (inside filter) | $.store.book[?(@.isbn)] |
JSONPath ile jq ve XPath Karşılaştırması
JSONPath, jq ve XPath farklı biçimler ve kullanım senaryoları için aynı problemi (yapılandırılmış verileri sorgulama) çözer. JSONPath, JSON'u hedef alır ve çoğu dilde kütüphane olarak kullanılabilir. jq, kendi Turing-complete filtre diliyle birlikte gelen bağımsız bir CLI aracıdır. XPath, XML üzerinde çalışır ve W3C belirtim yığınının bir parçasıdır.
| Özellik | JSONPath | jq | XPath |
|---|---|---|---|
| Veri biçimi | JSON | JSON | XML |
| İlk öğeye erişim | $.store.book[0] | .store.book[0] | /store/book[1] |
| Özyinelemeli arama | $..price | .. | .price? | //price |
| Filtre ifadesi | [?(@.price<10)] | select(.price < 10) | [price<10] |
| Belirtim | RFC 9535 | stedolan.github.io | W3C XPath 3.1 |
Kod Örnekleri
Popüler dillerde ve araçlarda JSONPath ifadelerini nasıl değerlendireceğiniz. Her örnek karşılaştırma için aynı kitapçı JSON yapısını kullanır.
import { JSONPath } from 'jsonpath-plus';
const data = {
store: {
book: [
{ title: 'Moby Dick', price: 8.99 },
{ title: 'The Great Gatsby', price: 12.99 }
]
}
};
// Get all book titles
JSONPath({ path: '$.store.book[*].title', json: data });
// → ['Moby Dick', 'The Great Gatsby']
// Recursive descent — find every price in the document
JSONPath({ path: '$..price', json: data });
// → [8.99, 12.99]
// Filter — books under $10
JSONPath({ path: '$.store.book[?(@.price < 10)]', json: data });
// → [{ title: 'Moby Dick', price: 8.99 }]from jsonpath_ng.ext import parse
import json
data = {
"store": {
"book": [
{"title": "Moby Dick", "price": 8.99},
{"title": "The Great Gatsby", "price": 12.99}
]
}
}
# Get all book titles
expr = parse("$.store.book[*].title")
titles = [match.value for match in expr.find(data)]
# → ['Moby Dick', 'The Great Gatsby']
# Recursive descent — all prices
expr = parse("$..price")
prices = [match.value for match in expr.find(data)]
# → [8.99, 12.99]
# Filter — books under $10
expr = parse("$.store.book[?price < 10]")
cheap = [match.value for match in expr.find(data)]
# → [{"title": "Moby Dick", "price": 8.99}]package main
import (
"fmt"
"github.com/ohler55/ojg/jp"
"github.com/ohler55/ojg/oj"
)
func main() {
src := `{
"store": {
"book": [
{"title": "Moby Dick", "price": 8.99},
{"title": "The Great Gatsby", "price": 12.99}
]
}
}`
obj, _ := oj.ParseString(src)
// Get all book titles
expr, _ := jp.ParseString("$.store.book[*].title")
results := expr.Get(obj)
fmt.Println(results)
// → [Moby Dick The Great Gatsby]
// Recursive descent — all prices
expr2, _ := jp.ParseString("$..price")
fmt.Println(expr2.Get(obj))
// → [8.99 12.99]
}# jq uses its own syntax, not JSONPath, but solves the same problem.
# Mapping common JSONPath patterns to jq:
# $.store.book[*].title → get all titles
echo '{"store":{"book":[{"title":"Moby Dick"},{"title":"Gatsby"}]}}' | \
jq '.store.book[].title'
# → "Moby Dick"
# → "Gatsby"
# $..price → recursive descent for "price" keys
echo '{"a":{"price":1},"b":{"price":2}}' | \
jq '.. | .price? // empty'
# → 1
# → 2
# Filter: books where price < 10
echo '{"store":{"book":[{"title":"A","price":8},{"title":"B","price":12}]}}' | \
jq '.store.book[] | select(.price < 10)'
# → {"title":"A","price":8}