Why Choose XPath?

Avato January 8, 2019
Why XPath?

XPath is a specification for a query language that locates and extracts data from XML documents, and a comprehensive set of functions for the manipulation of that data. XPath is used to identify, filter, and test nodes and content; and to apply functions or operations on the resulting data sets.

XML provides tree-structured data objects. Each tag in an XML document is a “node” in the tree. A tag can contain attributes, other tags, and raw data content: these are also nodes in the tree. A valid XML document has exactly one node at the “root” of its tree. Contained by that node are its “children” and, contained in those children, “descendent” nodes branching out until reaching terminal nodes that contain only data content.

An Example of a Tree Structure

└ root
  └ recipeCollection
    ├ recipe
    │ ├ title
    │ ├ ingredients
    │ │ ├ ingredient
    │ │ └ ingredient ...
    │ ├ preparation
    │ │ ├ step
    │ │ └ step ...
    │ └ notes
    │   └ note ...
    ├ recipe
    │ ├ title
    │ ├ ingredients
    │ │ ├ ingredient
    │ │ └ ingredient ...
    │ ├ preparation
    │ │ ├ step
    │ │ └ step ...
    │ └ notes
    │   └ note ...
    ├ ...
    ┆

XPath provides a language with which to locate nodes, by identifying the node’s “address” or by finding nodes using tests or filters, and to perform operations on the identified nodes.

In identifying the location of a node, XPath uses the concept of an “axis,” which describes the relationship between the node that is currently identified (the “context”) and the node that one wants to locate. These axes include familial relations (“parent”, “child”, “descendent”, “sibling”, etc.), linear relations (“preceding”, “following”, etc.), and two XML markup identifies (“attribute” and “namespace”). Combined with tests and filters, XPath provides an immensely powerful but succinct way to exactly identify a specific node or collection of nodes.

An example XPath statement can illustrate this power. The only further knowledge you require is that the slash character is used to separate steps in the traversal; double colons separate axes from tag names; and square brackets contain filtering statements.

The following XPath statement returns all the ingredients for the second recipe:

/root/recipeCollection/recipe[2]/descendent::ingredient

This XPath statement returns a set of recipes which have less than five ingredients.
Here, instead of “descendent::” we use the abbreviation “//”.

root//recipe[count(//ingredient) lt 5]

Let’s extend the example recipeCollection tree by stating that ingredient nodes contain name and amount tags, and step nodes refer to an ingredient using a name tag:

...
<ingredient>
  <name>Paprika</name>
  <amount>1 tsp</amount>
</ingredient>
...
<step>Add <name>Paprika</name> to the dry ingredients.</step>
...

If the current context is the step node, this XPath expression will fetch the amount of paprika required by the step. Note that “self” refers to the context node:

ancestor::recipe//ingredient[name = self::*/name]/amount

In verbose English, this location statement says “find the recipe that is the ancestor to this step and look through that recipes descendents for an ingredient that has the same name as the name used in this step, then return the amount listed for that ingredient.”

In addition to its powerful tree navigation axes, XPath is complemented by a full suite of functions that perform filtering and computation, as well as expressions providing conditional tests, variable assignment, looping constructs, data type casting, etceteras.

In spite of its immense expressive power, XPath is both succinct and understandable, as demonstrated in this list of sample expressions:

//list[parent::list]A collection of lists that are within lists
row[position() mod 2 = 0]Identifies an even-numbered row
//img[not(@alt)Identifies all images that lack an alternative text attribute (“@” is shorthand for “attribute::”)
bin[content = 'nails'][status = 'full'][1]Selects the first bin that is full of nails
sum(for $i in item return $i/@price * $i/@qty)Return the total cost for a collection of items with price and quantity attributes
distinct-values($items1[not(. = $items2)])Given two sets of nodes, return only those nodes in the first set that are not in the second set (“.” is shorthand for “self::”)
format-number($number,' 0000000.00 ;(0000000.00)')Given a number, render it in accounting format.
some $s in //section
satisfies
every $id in //section[@id ne $s/@id]/@id
satisfies
$id = $s/ref/@idref
Returns true if there is a section that references (with ref tags) every section except itself
for $cust in doc('customer.xml')//customer
return (
$cust/id/text(),
sum(
for $ord in doc('orders.xml')//order[custID eq $cust/id] return ($ord/total)
)
)
For each customer, return their ID and the total for their orders; note that the customer list and orders lists are in separate files

The expressive power of XPath can not be overstated: it is an incredibly powerful tool for navigating and filtering XML documents. It is, in fact, a Turing-complete language: this means that it can perform any computation, the same as any other programming language. That is not to say that XPath is any sort of sensible choice for writing general-purpose applications — but is by far the best choice for any application involving the manipulation of XML documents.

It is worth noting that while XPath is a computation language, it is not one that stands alone. XPath is always used within a host language: it is the host language that is responsible for doing useful things with an XPath computation.

Why XPath?

Because XPath is absolutely the best way to extract information from XML data structures.

David Priest

JSON vs XML

May 7, 2019
READ MORE
Avato

Advantages of XSLT

January 15, 2019
READ MORE