مختبر XPath
اختبر تعبيرات XPath على XML وشاهد جميع العقد المطابقة
إدخال XML
ما هو XPath؟
XPath (لغة مسار XML) هي لغة استعلام لتحديد العقد من مستند XML. حدّدها اتحاد W3C ضمن معيار XSLT، وتتعامل مع مستند XML باعتباره شجرة من العقد، وتوفّر صياغة مسارية للتنقل في تلك الشجرة. فمثلاً، التعبير //book[@category="fiction"]/title يختار كل عنصر title داخل عنصر book تكون قيمة سمة category فيه مساوية لـ "fiction". تُعدّ النسخة 1.0، المدعومة من جميع المتصفحات الرئيسية ومعظم مكتبات XML، نسخةً صادرة كتوصية W3C عام 1999 وتبقى الأوسع انتشاراً حتى اليوم.
تُعيد تعبيرات XPath أحد أربعة أنواع من النتائج: مجموعات العقد، والنصوص، والأرقام، والقيم المنطقية. مجموعة العقد هي النتيجة الأكثر شيوعاً وتحتوي على عقدة XML واحدة أو أكثر، وقد تكون فارغة (العناصر، والسمات، وعقد النص، والتعليقات، وتعليمات المعالجة). تصدر نتائج النص والرقم والقيمة المنطقية عن دوال XPath كـ count() وsum() وcontains() والمقارنات المنطقية. يجعل هذا نظامُ الأنواع XPath مناسباً لاستخراج البيانات وكتابة المنطق الشرطي في ملفات أنماط XSLT وتوكيدات XML Schema.
تعمل تعبيرات النسخة 1.0 في كل مكان: document.evaluate() في JavaScript، وlxml وElementTree في Python، وjavax.xml.xpath في Java، وDOMXPath في PHP، وأدوات سطر الأوامر كـ xmllint. تضيف النسختان 2.0 و3.1 أنظمة أنواع ودوالاً أكثر ثراءً لكنهما تستلزمان محركات متخصصة كـ Saxon. استخدم النسخة 1.0 افتراضياً، ولا تلجأ إلى 2.0 أو 3.1 إلا حين تحتاج إلى متتاليات أو تعبيرات نمطية أو دوال عليا — شريطة قبولك الاعتماد على Saxon أو BaseX.
لماذا تستخدم مختبر XPath أونلاين؟
كتابة تعبيرات صحيحة تستلزم فهم بنية المستند واختباره ببيانات حقيقية. تمنحك هذه الأداة بيئة تفاعلية للتكرار على التعبيرات دون الحاجة إلى إعداد مشروع أو كتابة شيفرة أساسية.
حالات استخدام مختبر XPath
مرجع محاور XPath
يحدد المحور اتجاه التنقل بالنسبة للعقدة الحالية (عقدة السياق). الصياغة الكاملة هي axis::node-test[predicate]، وإن كان معظم المطورين يستخدمون الصيغ المختصرة (//, @, ..) يومياً. يسرد هذا الجدول جميع محاور النسخة 1.0 مع أمثلة عملية.
| المحور | مثال | الوصف |
|---|---|---|
| child | child::book | Direct children named "book" |
| descendant | descendant::title | All "title" elements at any depth |
| parent | parent::* | The parent of the current node |
| ancestor | ancestor::catalog | All ancestors named "catalog" |
| following-sibling | following-sibling::book | Siblings after the current node |
| preceding-sibling | preceding-sibling::book | Siblings before the current node |
| attribute | attribute::lang | The "lang" attribute of the node |
| self | self::book | The current node if it is "book" |
| descendant-or-self | //title | Shorthand: any "title" in the tree |
| ancestor-or-self | ancestor-or-self::* | Current node plus all ancestors |
دوال XPath 1.0
تعرّف اللغة 27 دالة مدمجة موزعة على أربع فئات: مجموعة العقد، والنص، والرقم، والقيمة المنطقية. هذه هي الدوال الأكثر استخداماً عند كتابة الاستعلامات، وجميعها مدعومة من هذا المختبر ومن كل تطبيق متوافق.
| الدالة | الفئة | الوصف |
|---|---|---|
| text() | Node test | Selects text content of a node |
| position() | Numeric | Returns 1-based position in node set |
| last() | Numeric | Returns size of current node set |
| count() | Numeric | Counts nodes: count(//book) |
| contains() | String | contains(@class, "active") — substring match |
| starts-with() | String | starts-with(title, "The") — prefix match |
| normalize-space() | String | Strips leading/trailing whitespace |
| string-length() | Numeric | Returns character count of a string |
| sum() | Numeric | sum(//price) — totals numeric values |
| not() | Boolean | not(@disabled) — negation |
| name() | String | Returns element or attribute name |
أمثلة برمجية
تُظهر هذه الأمثلة كيفية تقييم التعبيرات برمجياً. تستخدم كل مقتطفة بنية catalog.xml ذاتها حتى تتمكن من مقارنة الفوارق بين الواجهات البرمجية عبر اللغات المختلفة.
const xml = `<catalog>
<book id="1"><title>1984</title><price>7.99</price></book>
<book id="2"><title>Dune</title><price>12.99</price></book>
</catalog>`
const parser = new DOMParser()
const doc = parser.parseFromString(xml, 'application/xml')
// Select all book titles
const result = doc.evaluate('//book/title', doc, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null)
let node = result.iterateNext()
while (node) {
console.log(node.textContent) // → "1984", "Dune"
node = result.iterateNext()
}
// Get a numeric value
const sum = doc.evaluate('sum(//price)', doc, null, XPathResult.NUMBER_TYPE, null)
console.log(sum.numberValue) // → 20.98from lxml import etree
xml = """<catalog>
<book id="1"><title>1984</title><price>7.99</price></book>
<book id="2"><title>Dune</title><price>12.99</price></book>
</catalog>"""
tree = etree.fromstring(xml.encode())
# Select elements by attribute
books = tree.xpath('//book[@id="1"]/title/text()')
print(books) # → ['1984']
# Use predicates with position
first = tree.xpath('//book[1]/title/text()')
print(first) # → ['1984']
# Boolean check
has_cheap = tree.xpath('boolean(//price[. < 10])')
print(has_cheap) # → Trueimport javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.*;
import org.w3c.dom.*;
var factory = DocumentBuilderFactory.newInstance();
var builder = factory.newDocumentBuilder();
var doc = builder.parse(new java.io.File("catalog.xml"));
var xpath = XPathFactory.newInstance().newXPath();
// Select node set
NodeList titles = (NodeList) xpath.evaluate(
"//book/title", doc, XPathConstants.NODESET
);
for (int i = 0; i < titles.getLength(); i++) {
System.out.println(titles.item(i).getTextContent());
// → "1984", "Dune"
}
// Evaluate to number
double total = (double) xpath.evaluate(
"sum(//price)", doc, XPathConstants.NUMBER
);
System.out.println(total); // → 20.98# Select all book titles xmllint --xpath '//book/title/text()' catalog.xml # → 1984Dune # Select by attribute xmllint --xpath '//book[@id="2"]/title' catalog.xml # → <title>Dune</title> # Count nodes xmllint --xpath 'count(//book)' catalog.xml # → 2 # Using xmlstarlet for formatted output xmlstarlet sel -t -v '//book/title' -n catalog.xml # → 1984 # → Dune