XPath Expressions

XPath Expressions

XPath expressions are many and varied, but they can be classified into two general groups:

The result expressions are interconvertible, but not symmetrically so. The interconversions are summarised:

From getNodeSet(...) getString(...) getNumber(...) getBoolean(...)
NodeSet self use getItem(0) via XPathString use set length
XPathString empty NodeSet self convert via Number
Number empty NodeSet convert double self via 0=
Boolean empty NodeSet "true"/"false" 0/1 self

This table is intended to indicate that NodeSet is the most information-rich of the result expressions, and that interconversions are not generally reversible. As an extreme example, if a NodeSet is converted to a Boolean, regardless of whether the result is true or false, it cannot be back-converted meaningfully to a NodeSet.

It is imperative, therefore, that all internal interconversions by generator expressions take account of this. In Limpid parlance, the main groups are:

XPath Principles

Before getting involved in detail, two points should be emphasised:

Context values are used intensively in XSLT to encapsulate the context of a node when it is used to evaluate an XPath expression and obtain a result expression (perhaps most commonly this is a NodeSet).

XPath Examples in Use

An XPath expression is always defined in terms of a string. Examples are:

Using an XPath Expression in Limpid

Limpid keeps the details of expression construction at arms length: it is always performed using a static function of the class XPathFactory: XPathFactory::getExpr(const DOMString& expressionString).

An expression is always evaluated in a , using a Context value. An example is:

Document dataDocument = getDocument(); // function is not specified here
  Context context;
  context.setNode(document);
  
  Expr expr = XPathFactory::getExpr("/meal/dessert[@topping = 'cream']");
  NodeSet resultSet = expr.getNodeSet(context);
  
  size_t length = resultSet.getLength();
  for (size_t index = 0; index <; length; ++index) {
    Node& node = resultSet.getItem(index);
    // ... display or process the node as required
  }

Using Expr to encapsulate an XPathExpression

The dominant characteristic of an XPath package is polymorphism; so we have to contend with:

The parent class of all XPath classes is XPathExpression (the unwieldly name is deliberate, to discourage its use). It specifies several interfaces, notably:

The return values are the standard return expressions referred to above, so there is no difficulty in returning one or other of them as a specific value. After all, we would not expect a Number to be returned by getNodeSet(...), would we?

But difficulties arise when we look more broadly at returning XPathExpressions, as in the previous example. Note that XPathFactory::getExpr() returns an Expr value, a class that we have not yet discussed.

The Expr Wrapper Class

Expr is a Handle or Bridge class, described in the standard texts. I can take credit (or blame) for reinventing it, but others may well have known the technique. In essence, it looks like an XPathExpression, in that it has the same interfaces, but it does not inherit from XPathExpression. It is simply a wrapper that delegates the standard function calls to its sole content: a Pointer<;XPathExpression>.

The point of this is that Expr is and always has the same size and can be returned as a value or added to an array or std container. The essentials of its definition are:

class Expr {
  public:
    Expr();
    Expr(const XPathExpression& expression);

    NodeSet getNodeSet(Context& ctxt, bool exact = true) const;
    XPathString getString(Context& ctxt) const;
    Boolean getBoolean(Context& ctxt) const;
    Number getNumber(Context& ctxt) const;

  private:
    Pointer<XPathExpression> xpathExpression_;
  };