JSONPath چیست؟
JSONPath یک زبان پرسوجو برای استخراج مقادیر از اسناد JSON است. این زبان در سال ۲۰۰۷ توسط Stefan Goessner به عنوان معادلی برای XPath در XML پیشنهاد شد و به شما امکان میدهد عبارتهایی مانند $.store.book[*].author بنویسید تا همه فیلدهای نویسنده درون یک آرایه کتاب را بدون نوشتن حلقه یا کد پیمایش دستی انتخاب کنید. این زبان در فوریه ۲۰۲۴ به عنوان RFC 9535 توسط IETF استانداردسازی شد.
یک عبارت JSONPath همیشه با $ شروع میشود که ریشه سند را نشان میدهد. از آنجا میتوانید انتخابگرها را زنجیر کنید: نشانهگذاری نقطهای برای کلیدهای شیء، نشانهگذاری براکتی برای شاخصهای آرایه، wildcard برای همه فرزندان، و recursive descent (..) برای جستجو در همه سطوح تودرتویی. عبارتهای فیلتر مانند [?(@.price < 10)] به شما اجازه میدهند عناصر را بر اساس شرایط ارزیابی شده روی مقادیرشان انتخاب کنید.
JSONPath در ابزارهای تست API مانند Postman، پلتفرمهای observability مانند Datadog و Splunk، موتورهای گردش کار مانند Apache NiFi، و کتابخانههای برنامهنویسی در JavaScript، Python، Go، Java و C# استفاده میشود. آزمایش عبارتهای خود روی دادههای واقعی پیش از تعبیه آنها در کد یا فایلهای پیکربندی، از خرابیهای بیصدا هنگامی که ساختار JSON با فرضهای شما مطابقت ندارد جلوگیری میکند.
چرا از این تستر JSONPath استفاده کنیم؟
نوشتن عبارتهای JSONPath به صورت دستی مستعد خطا است. یک نقطه گمشده، نوع براکت اشتباه، یا نحو فیلتر نادرست میتواند نتایج خالی بدون هیچ پیام خطایی برگرداند. این ابزار بازخورد بصری فوری درباره آنچه عبارت شما با آن تطابق دارد به شما میدهد.
موارد استفاده از JSONPath
مرجع نحو JSONPath
RFC 9535 نحو استاندارد JSONPath را تعریف میکند. جدول زیر عملگرهایی را که در اکثر پرسوجوها استفاده میکنید پوشش میدهد. همه عبارتها با $ (گره ریشه) شروع میشوند و یک یا چند انتخابگر را برای پیمایش ساختار سند زنجیر میکنند.
| عملگر | توضیح | مثال |
|---|---|---|
| $ | 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 در مقابل jq و XPath
JSONPath، jq و XPath یک مشکل مشابه (پرسوجو از دادههای ساختاریافته) را برای فرمتها و موارد استفاده متفاوت حل میکنند. JSONPath دادههای JSON را هدف قرار میدهد و به عنوان کتابخانه در اکثر زبانها در دسترس است. jq یک ابزار CLI مستقل با زبان فیلتر Turing-complete خودش است. XPath روی XML عمل میکند و بخشی از مجموعه مشخصات W3C است.
| ویژگی | JSONPath | jq | XPath |
|---|---|---|---|
| فرمت داده | JSON | JSON | XML |
| دسترسی به اولین عنصر | $.store.book[0] | .store.book[0] | /store/book[1] |
| جستجوی بازگشتی | $..price | .. | .price? | //price |
| عبارت فیلتر | [?(@.price<10)] | select(.price < 10) | [price<10] |
| مشخصات | RFC 9535 | stedolan.github.io | W3C XPath 3.1 |
مثالهای کد
نحوه ارزیابی عبارتهای JSONPath در زبانها و ابزارهای محبوب. هر مثال از همان ساختار JSON کتابفروشی برای مقایسه استفاده میکند.
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}