XML Tutorial · XPath, XSLT and XQuery · Lesson 12 of 28
XPath Basics
Selecting nodes with XPath: paths, the // shortcut, attributes, predicates, and the core functions.
Paths through the tree
XPath is a small language for pointing at parts of an XML document, like file paths for a tree. XPath 1.0 is built into lxml, PHP, Java and every browser.
| Expression | Selects |
|---|---|
/shop/product |
product children of the root shop |
//name |
every name element anywhere |
//product/@sku |
the sku attribute of every product |
//product[price > 10] |
products whose price is over 10 |
//product[@category='drinks']/name |
names of products in a category |
//product[1] |
the first product of each parent (positions start at 1) |
//product[last()] |
the last one |
count(//product) |
a number |
. / .. |
the current node / its parent |
* / @* / text() |
any element / any attribute / text nodes |
The same queries in three languages
from lxml import etree
shop = etree.fromstring(b"""<shop>
<product sku="TEA-01" category="drinks"><name>Green tea</name><price>3.50</price><stock>40</stock></product>
<product sku="COF-02" category="drinks"><name>Coffee beans</name><price>9.90</price><stock>0</stock></product>
<product sku="MUG-07" category="kitchen"><name>Camel mug</name><price>12.00</price><stock>15</stock></product>
<product sku="PAN-03" category="kitchen"><name>Frying pan</name><price>29.00</price><stock>4</stock></product>
</shop>""")
print("products:", int(shop.xpath("count(//product)")))
print("over 10:", shop.xpath("//product[price > 10]/name/text()"))
print("drinks:", shop.xpath("//product[@category='drinks']/@sku"))
print("out of stock:", shop.xpath("string(//product[stock = 0]/name)"))
print("total stock:", int(shop.xpath("sum(//stock)")))
Output
products: 4 over 10: ['Camel mug', 'Frying pan'] drinks: ['TEA-01', 'COF-02'] out of stock: Coffee beans total stock: 59
<?php
$doc = new DOMDocument();
$doc->loadXML('<shop>
<product sku="TEA-01" category="drinks"><name>Green tea</name><price>3.50</price><stock>40</stock></product>
<product sku="COF-02" category="drinks"><name>Coffee beans</name><price>9.90</price><stock>0</stock></product>
<product sku="MUG-07" category="kitchen"><name>Camel mug</name><price>12.00</price><stock>15</stock></product>
<product sku="PAN-03" category="kitchen"><name>Frying pan</name><price>29.00</price><stock>4</stock></product>
</shop>');
$xp = new DOMXPath($doc);
$list = fn($q) => "['" . implode("', '", array_map(fn($n) => $n->nodeValue, iterator_to_array($xp->query($q)))) . "']";
echo "products: ", $xp->evaluate("count(//product)"), "\n";
echo "over 10: ", $list("//product[price > 10]/name/text()"), "\n";
echo "drinks: ", $list("//product[@category='drinks']/@sku"), "\n";
echo "out of stock: ", $xp->evaluate("string(//product[stock = 0]/name)"), "\n";
echo "total stock: ", $xp->evaluate("sum(//stock)"), "\n";
Output
products: 4 over 10: ['Camel mug', 'Frying pan'] drinks: ['TEA-01', 'COF-02'] out of stock: Coffee beans total stock: 59
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import java.io.ByteArrayInputStream;
import java.util.*;
public class Main {
static XPath xp = XPathFactory.newInstance().newXPath();
static String list(String query, Document doc) throws Exception {
NodeList nodes = (NodeList) xp.evaluate(query, doc, XPathConstants.NODESET);
List<String> values = new ArrayList<>();
for (int i = 0; i < nodes.getLength(); i++) values.add(nodes.item(i).getNodeValue());
return "['" + String.join("', '", values) + "']";
}
public static void main(String[] args) throws Exception {
String xml = """
<shop>
<product sku="TEA-01" category="drinks"><name>Green tea</name><price>3.50</price><stock>40</stock></product>
<product sku="COF-02" category="drinks"><name>Coffee beans</name><price>9.90</price><stock>0</stock></product>
<product sku="MUG-07" category="kitchen"><name>Camel mug</name><price>12.00</price><stock>15</stock></product>
<product sku="PAN-03" category="kitchen"><name>Frying pan</name><price>29.00</price><stock>4</stock></product>
</shop>""";
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
System.out.println("products: " + ((Double) xp.evaluate("count(//product)", doc, XPathConstants.NUMBER)).intValue());
System.out.println("over 10: " + list("//product[price > 10]/name/text()", doc));
System.out.println("drinks: " + list("//product[@category='drinks']/@sku", doc));
System.out.println("out of stock: " + xp.evaluate("string(//product[stock = 0]/name)", doc));
System.out.println("total stock: " + ((Double) xp.evaluate("sum(//stock)", doc, XPathConstants.NUMBER)).intValue());
}
}
Output
products: 4 over 10: ['Camel mug', 'Frying pan'] drinks: ['TEA-01', 'COF-02'] out of stock: Coffee beans total stock: 59
Run this on your computer — see “Get Started” for setup.
Common mistakes
//searches the whole document from the root; inside a loop use.//name(relative to the current node).//product[1]is "the first product of each parent", not "the first product in the document": use(//product)[1]for that.- Comparing a node set with a value is true if any node matches, so
//price != 3.50is true almost always. - Namespaced documents need prefixes in every step; see namespaces.
Test yourself
Which XPath selects the very first product in the whole document?
(//product)[1]//product[1]/product[0]//product[first()]
Show answer
Answer: (//product)[1]
//product[1] picks the first product child of each parent; parentheses apply the position to the whole set.