Skip to content

XPath Axes — Navigate XML Trees Like a Pro

DodaTech Updated 2026-06-23 8 min read

In this tutorial, you'll learn about XPath Axes. We cover key concepts, practical examples, and best practices.

XPath axes define the direction of navigation relative to the current node, enabling complex tree traversal beyond simple parent-child relationships — from ancestor lookups to following-sibling and namespace axis queries.

What You'll Learn

  • The 13 XPath axes and how each one navigates the XML tree
  • Using the full axis syntax vs abbreviated shortcuts
  • Combining axes with node tests and predicates
  • Real-world patterns for efficient axis-based queries

Why XPath Axes Matter

Simple XPath like /library/book/title only goes downward. Axes let you move up, sideways, and across the entire tree from any starting point. For example, finding "the next sibling element after the current one" or "the nearest ancestor with a specific attribute" requires axis navigation. Durga Antivirus Pro uses XPath axes to navigate complex XML threat signature files, walking the ancestor chain to determine the context of suspicious nodes during scans.

Learning Path

flowchart LR
  A[XPath Basics] --> B[XPath Functions]
  B --> C[XPath Axes
You are here] C --> D[XQuery FLWOR] D --> E[XML Schema XSD]

The Complete Axes Reference

XPath defines 13 axes for navigation:

Axis Abbreviation Selects
child:: (default) Direct children
parent:: .. The parent node
descendant:: // All descendants
ancestor:: (none) All ancestors up to root
descendant-or-self:: // (on its own) Self plus descendants
ancestor-or-self:: (none) Self plus ancestors
following-sibling:: (none) Siblings after current
preceding-sibling:: (none) Siblings before current
following:: (none) All nodes after in document order
preceding:: (none) All nodes before in document order
attribute:: @ Attributes of current node
namespace:: (none) Namespace nodes
self:: . The current node itself

Working Example XML

<?xml version="1.0" encoding="UTF-8"?>
<library>
    <book category="fiction" id="b1">
        <title>The Hobbit</title>
        <author>J.R.R. Tolkien</author>
        <year>1937</year>
        <price currency="USD">12.99</price>
    </book>
    <book category="non-fiction" id="b2">
        <title>A Brief History of Time</title>
        <author>Stephen Hawking</author>
        <year>1988</year>
        <price currency="GBP">9.99</price>
    </book>
    <book category="fiction" id="b3">
        <title>1984</title>
        <author>George Orwell</author>
        <year>1949</year>
        <price currency="USD">10.99</price>
    </book>
</library>

Forward Axes

Forward axes select nodes that appear after the current node in document order.

descendant::

Selects all children, grandchildren, and deeper descendants:

/library/descendant::price
# Result: all 3 price elements
# Abbreviated: //price

/library/book/descendant::text()
# Result: all text content inside each book

following-sibling::

Selects siblings after the current node:

/library/book[1]/following-sibling::book
# Result: b2 (A Brief History of Time) and b3 (1984)

/library/book["@category"='fiction'][1]/following-sibling::*
# Result: b2 and b3 (all siblings after first fiction book)

/library/book[1]/following-sibling::book[1]/title
# Result: "A Brief History of Time" (immediately next sibling)

following::

Selects all nodes after the current in document order, excluding descendants:

/library/book[1]/price/following::*
# Result: author, year, price of b2; title, author, year, price of b3
# (everything after the first price node, depth-first)

/library/book[1]/price/following::book
# Result: b2 and b3

Reverse Axes

Reverse axes select nodes before the current node.

ancestor::

/library/book[2]/title/ancestor::*
# Result: book (b2), library (root)

/library/book[2]/title/ancestor::library
# Result: library element

/library/book[2]/title/ancestor::book/@category
# Result: non-fiction

preceding-sibling::

/library/book[3]/preceding-sibling::book
# Result: b1 and b2 (books before 1984)

/library/book["@category"='non-fiction']/preceding-sibling::book
# Result: b1 (The Hobbit — the fiction book that comes before)

/library/book[3]/preceding-sibling::book[1]/title
# Result: "A Brief History of Time" (immediately preceding sibling)

preceding::

/library/book[2]/title/preceding::*
# Result: library, b1, title of b1, author of b1, year of b1, price of b1
# (all nodes before the title of b2, in reverse document order)

Combining Axes with Node Tests and Predicates

Axes work with node tests and predicates for precise queries:

# Find the nearest ancestor that is a book
/library/book[2]/title/ancestor::book[1]
# Result: b2 (A Brief History of Time)

# Find all preceding sibling book elements of the last book
/library/book[last()]/preceding-sibling::book["@category"='fiction']
# Result: b1 (The Hobbit)

# From a price node, find the parent book's title
/library/book[2]/price/parent::book/title
# Result: "A Brief History of Time"

# All descendants of library that are elements (not text)
/library/descendant::*
# Result: all 3 books, all titles, authors, years, prices

Real-World Use: Contextual Navigation

In document processing, axes enable contextual lookups that would be complex with abbreviated syntax:

<report>
    <section id="s1">
        <title>Introduction</title>
        <paragraph>First paragraph.</paragraph>
        <paragraph>Second paragraph.</paragraph>
        <note>Important note about section 1.</note>
    </section>
    <section id="s2">
        <title>Analysis</title>
        <paragraph data-ref="s1">See introduction for background.</paragraph>
        <paragraph>Key findings here.</paragraph>
    </section>
</report>

Using axes for contextual navigation:

# From a paragraph, find the section title
//paragraph[2]/ancestor::section/title
# Result: "Introduction" (for paragraph 2 of section 1)

# Find all paragraphs that follow a note within the same section
//note/following-sibling::paragraph
# Result: (none in section 1 since note is last; the paragraph in section 2)

# Find the nearest preceding section from any paragraph
//paragraph["@data"-ref]/preceding::section[1]
# Result: s1 (the section before the paragraph with data-ref)

# All paragraphs that are the first child of their section
//section/paragraph[1]
# Result: both first paragraphs of each section

Security Angle

XPath axes can be exploited in injection attacks. A malicious input like ' or 1=1 or ' in a predicate combined with axes navigation can traverse the entire XML tree, exposing unauthorized data. Durga Antivirus Pro uses parameterized XPath with axes restricted to a subtree to prevent injection-based lateral movement through the document tree. Always apply the principle of least privilege to XPath queries — restrict axis navigation to the minimum tree scope needed.

Using Axes in Python

Python's lxml library supports full XPath axis syntax:

from lxml import etree

xml_data = """<?xml version="1.0" encoding="UTF-8"?>
<library>
    <book category="fiction" id="b1">
        <title>The Hobbit</title>
        <author>J.R.R. Tolkien</author>
        <year>1937</year>
        <price>12.99</price>
    </book>
    <book category="non-fiction" id="b2">
        <title>A Brief History of Time</title>
        <author>Stephen Hawking</author>
        <year>1988</year>
        <price>9.99</price>
    </book>
</library>"""

root = etree.fromstring(xml_data)

# Use XPath axes to find ancestor context
for price in root.findall('.//price'):
    parent_book = price.xpath('parent::book')
    if parent_book:
        title = parent_book[0].find('title').text
        print(f"Price {price.text} is from: {title}")

# Expected output:
# Price 12.99 is from: The Hobbit
# Price 9.99 is from: A Brief History of Time

Performance Considerations

Axes like descendant:: and following:: scan large portions of the tree:

Axis Performance Best Use
child:: Fast Direct children only
parent:: Fast Single parent lookup
descendant:: Moderate Deep tree scanning
ancestor:: Fast Upward navigation
following-sibling:: Moderate Peer navigation
preceding-sibling:: Moderate Reverse peer navigation
following:: Slow Full tree scan
preceding:: Slow Full tree scan

For large documents, prefer axes that limit their scope. Use following:: and preceding:: sparingly as they scan the entire remaining document.

Common Mistakes

1. Forgetting that axes are relative to the context node

<!-- In a template matching /library/book -->
<!-- following-sibling::book selects other books -->
<!-- child::* would be wrong here — books don't have book children -->

2. Confusing following with following-sibling

following:: selects all nodes after the current node in the entire document. following-sibling:: selects only siblings at the same level.

3. Document order vs reverse document order

Forward axes return results in document order. Reverse axes (ancestor, preceding-sibling, preceding) return results in reverse document order.

4. Using descendant:: when child:: suffices

descendant:: scans the entire subtree. If you only need direct children, use child:: for better performance.

Practice Questions

  1. What is the difference between following:: and following-sibling::? following:: selects all nodes after the current in document order anywhere in the tree. following-sibling:: selects only siblings at the same depth level.

  2. Which axis does the .. abbreviation represent? The parent:: axis. ../title is equivalent to parent::*/child::title.

  3. Why might descendant:: be slower than child::? descendant:: traverses the entire subtree recursively, while child:: only checks direct children.

Challenge: Starting from a price node, write XPath expressions that find the parent book's title, the preceding book's title, the library element, and all prices in following books — using only explicit axis syntax.

FAQ

What are XPath axes?

XPath axes define the direction of navigation relative to the current node, such as child, parent, ancestor, descendant, following-sibling, and attribute.

How many XPath axes are there?

There are 13 axes: child, parent, descendant, ancestor, descendant-or-self, ancestor-or-self, following-sibling, preceding-sibling, following, preceding, attribute, namespace, and self.

What is the default axis in XPath?

The child:: axis is the default. book/title is equivalent to child::book/child::title.

How do I navigate to a parent node in XPath?

Use the parent:: axis or the shorthand ... For example, parent::book or ../@category.

What is the difference between descendant and child axis?

child:: selects only direct children. descendant:: selects all children at any depth — grandchildren, great-grandchildren, and so on.

What axis does @ represent?

The @ symbol is shorthand for the attribute:: axis. @category equals attribute::category.

Try It Yourself

Test axis navigation interactively with Python:

from lxml import etree

xml = """<?xml version="1.0"?>
<root>
    <item id="1">
        <sub>A</sub>
    </item>
    <item id="2">
        <sub>B</sub>
    </item>
</root>"""

root = etree.fromstring(xml)
item1 = root.find("item["@id"='1']")

# Forward axis test
following = item1.xpath("following-sibling::item/sub/text()")
print("Following siblings:", following)

# Expected:
# Following siblings: ['B']

# Reverse axis test
item2 = root.find("item["@id"='2']")
preceding = item2.xpath("preceding-sibling::item/sub/text()")
print("Preceding siblings:", preceding)

# Expected:
# Preceding siblings: ['A']

What's Next

Tutorial What You'll Learn
XPath Explained — Querying XML XPath basics and predicates
XSLT Explained — Transform XML Transform XML into HTML and other formats
XML Parsing in Python Process XML data with Python's ElementTree and lxml

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-23.

What's Next

Congratulations on completing this XPath Axes tutorial! Here's where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Create a script that uses axes to extract contextual data
  • Explore related topics — Check out other XML tutorials in this category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro