JSONPath Tutorial: Query JSON Like a Pro
Master JSONPath syntax with practical examples. Learn every operator, filter expression, and real-world query pattern to extract data from complex JSON structures.
ما هو JSONPath؟
JSONPath هي لغة استعلام لـ JSON، مشابهة لما تفعله XPath مع XML. تتيح لك الوصول إلى بنية JSON معقّدة واستخراج البيانات التي تحتاجها بالضبط باستخدام صياغة تعبيرات موجزة. بدلًا من كتابة حلقات وشروط للتنقّل بين الكائنات والمصفوفات المتداخلة، تكتب تعبير مسار واحد مثل $.store.book[?(@.price < 10)].title وتسترجع كل عنوان كتاب بسعر أقل من عشرة دولارات.
قدّم Stefan Goessner لغة JSONPath عام 2007، ومنذ ذلك الحين أصبحت معيارًا عمليًا عبر اللغات والأدوات. إذا كنت تعمل مع واجهات REST أو ملفات الإعدادات أو أي بيانات JSON غير بسيطة، فستوفر عليك JSONPath وقتًا كبيرًا.
JSONPath مقابل XPath: مقارنة سريعة
إذا كنت تعرف XPath مسبقًا، فستجد JSONPath مألوفة. الفرق الأساسي أن JSON يمتلك نموذج بيانات أبسط — كائنات ومصفوفات بدل العناصر والسمات والمساحات الاسمية. إليك كيف تقارن اللغتان:
| المفهوم | XPath | JSONPath |
|---|---|---|
| الجذر | / | $ |
| الطفل | /child | .child أو ['child'] |
| النزول التكراري | // | .. |
| البدل العام | * | * |
| فهرس المصفوفة | [1] (يبدأ من 1) | [0] (يبدأ من 0) |
| التصفية | [predicate] | [?(expression)] |
مرجع شامل لمشغّلات JSONPath
كل تعبير JSONPath يبدأ من الجذر $ ويربط المشغّلات للتنقّل أعمق. إليك كل المشغّلات التي تحتاج معرفتها:
| المشغّل | الوصف | مثال |
|---|---|---|
$ | الكائن الجذر أو المصفوفة | $ — المستند بأكمله |
.key | عضو فرعي بالاسم (صياغة النقطة) | $.store.name |
['key'] | عضو فرعي بالاسم (صياغة الأقواس) | $['store']['name'] |
.. | النزول التكراري — يبحث في كل الأحفاد | $..price — كل حقل "price" |
* | البدل العام — كل أعضاء كائن أو مصفوفة | $.store.* |
[n] | فهرس المصفوفة (يبدأ من الصفر) | $.items[0] |
[start:end] | شريحة المصفوفة (النهاية غير مشمولة) | $.items[0:3] — أول ثلاثة عناصر |
[start:end:step] | شريحة المصفوفة مع خطوة | $.items[::2] — كل عنصر آخر |
[n,m] | فهارس مصفوفة متعددة | $.items[0,2,4] |
[?()] | تعبير تصفية | $.items[?(@.price > 10)] |
@ | العقدة الحالية (يُستخدم داخل التصفيات) | @.name == 'Alice' |
JSON نموذجي لأمثلتنا
سنستخدم هذا المستند JSON طوال الدرس. يمثّل استجابة واجهة برمجة تطبيقات صغيرة لمكتبة:
{
"store": {
"name": "TechBooks Online",
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95,
"inStock": true
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99,
"inStock": false
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99,
"inStock": true
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99,
"inStock": true
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
أمثلة عملية على JSONPath
1. الحصول على اسم المتجر
$.store.name
النتيجة:
"TechBooks Online"
2. الحصول على كل عناوين الكتب
$.store.book[*].title
النتيجة:
["Sayings of the Century","Sword of Honour","Moby Dick","The Lord of the Rings"]
3. الحصول على أول كتاب
$.store.book[0]
النتيجة: كائن أول كتاب بالكامل.
4. الحصول على آخر كتاب
$.store.book[-1]
الفهارس السالبة تُحسب من النهاية. -1 يعطيك آخر عنصر.
5. شريحة: أول كتابين
$.store.book[0:2]
تُرجع الكتب عند الفهرس 0 و1. فهرس النهاية غير مشمول، كما في Python وslice() في JavaScript.
6. الحصول على كل الأسعار في المستند بأكمله
$..price
النتيجة:
[8.95, 12.99, 8.99, 22.99, 19.95]
مشغّل النزول التكراري .. يجد كل حقل price بغضّ النظر عن العمق — بما في ذلك سعر الدراجة.
7. تصفية: كتب بأقل من 10 دولارات
$.store.book[?(@.price < 10)]
تُرجع الكتابين بسعري 8.95 و8.99. الرمز @ يشير إلى العنصر الحالي قيد التقييم.
8. تصفية: كتب لها ISBN
$.store.book[?(@.isbn)]
تُرجع فقط Moby Dick وThe Lord of the Rings.
9. تصفية: كتب خيال متوفرة في المخزون
$.store.book[?(@.category == 'fiction' && @.inStock == true)]
يمكنك دمج الشروط بـ && (و) و|| (أو).
10. فهارس متعددة
$.store.book[0,3]
اختيار عناصر محددة. تُرجع الكتاب الأول والرابع.
11. بدل عام على كائن
$.store.bicycle.*
النتيجة:
["red", 19.95]
تُرجع كل قيم كائن الدراجة.
12. الحصول على مؤلفي الكتب الأغلى
$.store.book[?(@.price > 15)].author
النتيجة:
["J. R. R. Tolkien"]
جرّب بنفسك: الصق JSON النموذجي أعلاه وجرّب هذه التعبيرات في مختبر JSONPath.
تعبيرات التصفية بالتفصيل
تعبيرات التصفية هي الجزء الأقوى في JSONPath. توجد داخل [?()] وتقيّم شرطًا منطقيًا لكل عنصر. إليك المشغّلات التي يمكنك استخدامها:
| المشغّل | المعنى | مثال |
|---|---|---|
== | يساوي | @.status == 'active' |
!= | لا يساوي | @.role != 'admin' |
> | أكبر من | @.age > 18 |
>= | أكبر من أو يساوي | @.score >= 90 |
< | أقل من | @.price < 50 |
<= | أقل من أو يساوي | @.quantity <= 0 |
=~ | مطابقة تعبير منتظم (في بعض التنفيذات) | @.name =~ /^J.*/ |
&& | منطقي AND | @.price > 5 && @.price < 20 |
| ` | ` |
نمط شائع هو التحقق من وجود الخاصية. كتابة @.isbn بدون مشغّل مقارنة تُرجع true إذا وُجدت الخاصية وليست null.
سيناريوهات من العالم الحقيقي
استخراج كل عناوين البريد من استجابة واجهة برمجة التطبيقات
لنفترض أنك تتلقى استجابة من واجهة إدارة مستخدمين فيها كائنات اتصال متداخلة بعمق:
{
"users": [
{
"name": "Alice",
"contacts": { "email": "alice@example.com", "phone": "555-0101" }
},
{
"name": "Bob",
"contacts": { "email": "bob@example.com", "phone": "555-0102" }
}
]
}
الاستعلام: $..email
النتيجة:
["alice@example.com", "bob@example.com"]
مشغّل النزول التكراري يجد كل حقل email أينما وُضع في التسلسل الهرمي.
العثور على منتجات حسب نطاق السعر
$.products[?(@.price >= 25 && @.price <= 100)]
مفيد عند تصفية استجابات الواجهة على العميل قبل عرض قائمة المنتجات.
الحصول على قيم إعدادات متداخلة
$.config.database.connections[0].host
التنقّل في ملفات الإعداد دون كتابة سلسلة وصول اختياري مثل config?.database?.connections?.[0]?.host.
JSONPath في لغات مختلفة
JavaScript / Node.js
حزمة jsonpath-plus هي التنفيذ الأكثر شيوعًا:
import { JSONPath } from "jsonpath-plus";
const data = { store: { book: [{ title: "Moby Dick", price: 8.99 }] } };
const titles = JSONPath({ path: "$.store.book[*].title", json: data });
console.log(titles); // ["Moby Dick"]
Python
استخدم jsonpath-ng لتنفيذ متوافق مع المعايير:
from jsonpath_ng.ext import parse
data = {"store": {"book": [{"title": "Moby Dick", "price": 8.99}]}}
expr = parse("$.store.book[*].title")
matches = [match.value for match in expr.find(data)]
print(matches) # ['Moby Dick']
Java
مكتبة Jayway JSONPath هي الخيار المعتاد لمشاريع JVM:
import com.jayway.jsonpath.JsonPath;
String json = "{\"store\":{\"book\":[{\"title\":\"Moby Dick\",\"price\":8.99}]}}";
List<String> titles = JsonPath.read(json, "$.store.book[*].title");
System.out.println(titles); // [Moby Dick]
مزالق وقيود
JSONPath قوية، لكن هناك نقاط يجب الانتباه إليها قبل الاعتماد عليها في الإنتاج:
- لا يوجد مواصفة معيارية (حتى وقت قريب). عُرّفت JSONPath بشكل غير رسمي، ونفّذتها المكتبات باختلافات دقيقة. نشر IETF RFC 9535 في 2024 لتوحيد الصياغة، لكن كثيرًا من المكتبات ما زالت تتبع مواصفة Goessner الأصلية. راجع دائمًا وثائق مكتبتك للميزات المدعومة.
- دعم التعبيرات المنتظمة يختلف. مشغّل
=~ليس مدعومًا في كل مكان. مكتبة Jayway في Java تدعمه؛ كثير من مكتبات JavaScript لا تدعمه. - لا توجد عمليات كتابة. JSONPath للقراءة فقط. لا يمكنك تعديل المستند الأصلي بها. للتغييرات تحتاج JSON Patch (RFC 6902) أو اجتيازًا يدويًا.
- الأداء على المستندات الضخمة. النزول التكراري (
..) يمسح كل عقدة في الشجرة. على مستندات بملايين العقد قد يكون بطيئًا. فضّل مسارات محددة عندما يهم الأداء. - عدم اتساق نوع الإرجاع. بعض المكتبات تُرجع قيمة واحدة للتطابق الواحد ومصفوفة لتعدد التطابقات. أخرى تُرجع دائمًا مصفوفة. طبّع نوع الإرجاع في كودك لتجنب المفاجآت.
- أحرف خاصة في المفاتيح. إذا احتوت مفاتيح JSON على نقاط أو مسافات أو أحرف خاصة أخرى، استخدم صياغة الأقواس:
$('my.key')بدلًا من$.my.key.
JSONPath مقابل jq
قد تتساءل كيف تقارن JSONPath بـ jq، معالج JSON سطر الأوامر الشائع. الجواب المختصر: يحلان مشاكل مشابهة لكن لجماهير مختلفة.
- JSONPath مصممة للتضمين في كود التطبيق. لها دعم مكتبات في كل لغة رئيسية وصياغة استعلام مباشرة.
- jq لغة تحويل بيانات كاملة. يمكنها التصفية والتعيين والاختزال وإعادة تشكيل JSON. أقوى لكن منحنى تعلم أشد وتُستخدم أساسًا في سطر الأوامر.
للاستعلام عن البيانات داخل تطبيق، JSONPath خيار عملي. لسكربتات الصدفة وخطوط البيانات، jq يصعب تجاوزه.
ورقة مرجعية سريعة
| ما تريد | JSONPath |
|---|---|
| المستند بأكمله | $ |
| خاصية محددة | $.user.name |
| كل عناصر المصفوفة | $.items[*] |
| أول عنصر | $.items[0] |
| آخر عنصر | $.items[-1] |
| نطاق من العناصر | $.items[1:4] |
| عناصر بخطوة في الشريحة (مثل كل عنصر ثانٍ) | $.items[::2] |
| كل حقول "name" في أي مكان | $..name |
| تصفية حسب القيمة | $.items[?(@.price < 10)] |
| تصفية حسب الوجود | $.items[?(@.discount)] |
| شروط متعددة | $.items[?(@.qty > 0 && @.active)] |
جرّب بنفسك: افتح مختبر JSONPath لتشغيل الاستعلامات على بيانات JSON الخاصة بك في الوقت الفعلي.