CSS selectors, JSONPath and jq as alternatives
XPath is not always the right tool: CSS is faster and simpler for downward-only selection, and JSON needs an entirely different query language.
CSS versus XPath
| Capability | CSS | XPath |
|---|---|---|
| Select by attribute | Yes | Yes |
| Select by text | No | [text()='x'] |
| Select a parent | No | parent:: or ancestor:: |
| Select a preceding sibling | No | preceding-sibling:: |
| Count matches | No | count() |
| Union of two selections | Yes, with , | Yes, with | |
| Speed in a browser | Native engine, fastest | Interpreter, slower |
| Comments and processing instructions | No | Yes |
from lxml import html
doc = html.fromstring("<ul><li class='on'>a</li><li>b</li></ul>")
print(doc.xpath("//li[@class='on']/text()")) # XPath
print(doc.cssselect("li.on")[0].text) # same result, shorter
# The one thing CSS cannot do here
print(doc.xpath("//li[.='b']/preceding-sibling::li/text()")) # ['a']- Reach for CSS first: it is shorter, faster and most front-end developers can maintain it.
- Switch to XPath when you need text matching, upward traversal or aggregation.
- In test frameworks, Playwright and Selenium accept both, so the deciding factor is usually readability.
JSONPath and jq
# JSONPath: the XPath-like syntax for JSON (RFC 9535)
# $ root
# .a.b child access
# [*] every element of an array
# ..x recursive descent
# [?(@.p > 1)] filter expression
# jq: a pipeline language, and the better default on the command line
curl -s https://api.example.com/books | jq '.items[] | select(.price > 20) | {id, title}'
# Recursive descent, the jq equivalent of //
jq '.. | objects | select(has("isbn")) | .isbn' books.json
# Aggregation, which XPath 1.0 cannot do at all
jq '[.items[].price] | {count: length, total: add, max: max}' books.json
# group and reshape
jq '[.items[] | {lang: .lang, title}] | group_by(.lang)
| map({lang: .[0].lang, n: length})' books.jsonimport json
from jsonpath_ng.ext import parse
data = json.load(open("books.json"))
expr = parse("$.items[?(@.price > 20)].title")
print([m.value for m in expr.find(data)])💡
The mental model differs more than the syntax: XPath navigates a typed tree with document order and node identity, while jq transforms a stream of values and JSONPath returns matches in array order. Prefer jq for pipelines and reports, JSONPath when a library needs a query string, and XPath when the data really is XML.
FAQ
Can I use CSS selectors on XML?
Only in libraries that implement CSS for XML, such as lxml's cssselect with an XML parser. Plain CSS has no concept of namespaces, which makes it unreliable on namespaced XML.
Is JSONPath standardised?
Yes, RFC 9535 (2024) defines JSONPath. Implementations still differ in extensions, so test the specific library you depend on rather than assuming a shared dialect.
Related
XPath in the browser: document.evaluate XPath performance: cost, indexes and //
Last refreshed 2026-09-18.