24.2. Location paths An instance of the XPath language is called an expression. XPath expressions can involve a variety of operations on different kinds of operands. In this chapter we will focus on two operand types: function calls and location paths. A location path is the most important kind of expression in the XPath notation. Its syntax is similar to the familiar path expressions used in URLs and in Unix and Windows systems to locate directories and files.[5] [5] There is an illustrated tutorial on path expressions in Chapter 17, "XPath primer", on page 384. 24.2.1 Basic concepts A location path has a starting point, which XPath calls its context node. In a file system path, it might be a computer, a disk drive, or a directory. In an XPath location path it could be, for example, the document element node or some other element node. The purpose of the location path is to select nodes from the document by locating the desired nodes relative to the initial context node. Arguably, the simplest location path is "/". This selects the root node (not the document element node). 24.2.1.1 Stepping down the hierarchy We can extend this location path to select the document element node instead of the root node. "/mydoc" will select a document element node named "mydoc". The name of an element node is the element-type name of the element it represents. Note ![](/books/2/559/1/html/2/images/013142193x/graphics/warning.jpg) | From now on, as long as we are discussing node trees, we'll often just say "element" instead of "element node". | We have taken a step "down" the tree. We can take another step: "/mydoc/section". This will select every section element that is a child of the mydoc element. Each slash-separated (/) path component is a step. Any amount of whitespace can be present between the parts of a location path. Steps can be written across a number of lines or spaced apart to be more legible to a reader. 24.2.1.2 Predicates So far we have seen how to build single and multi-level location paths based on element-type names. However, the type name is not the only thing that is interesting about an element. For example, we might want to filter out elements that have (or do not have) particular attributes with particular values. Or we may be interested in the first or seventh element, or just the even-numbered ones. We can express these constraints with qualifiers called predicates. Any step can be qualified. The location path in Example 24-2, for example, selects the seventh paragraph from each section with a security attribute whose string-value is "public". Example 24-2. Selecting the seventh para from each public section /mydoc/section[@security="public"]/para[7] 24.2.1.3 Selection Note that we use the word select carefully. We could say that the expression returns certain nodes but that might put a picture in your head of nodes being ripped out of the tree and handed to you: "Here are your nodes!" Rather, what you get back is a set of locations pointers to the nodes. Imagine the result of a location path as a set of arrows pointing into the node tree, saying: "Your nodes are here!" 24.2.1.4 Context The context node keeps changing as we step down the path. As each step is evaluated, the result is a set of nodes in XPath talk, a node-set. The node-set could have one or more nodes, or it could be empty. The next step is then evaluated for each member of that node-set. That is, each member is used as the context node for one of the evaluations of the next step. The node-sets selected by each of those evaluations are combined (except for any duplicates) to produce the result node-set. Consider what happens in Example 24-2. - 1. The XPath processor first evaluates the "/". The root node becomes the initial context node.
- 2. Next it looks for every child of the context node with the name "mydoc". There will be only one member of that node-set because XML allows only a single root element. It becomes the context node for the next step, which is evaluated only once.
- 3. Next the processor looks for all of the section children in the context of the mydoc element that have the appropriate attribute value and returns their node-set. The next step will be evaluated once for each selected section node, which is the context node for that evaluation.
- 4. We're almost done. The processor looks for the seventh para several times, once for each section in the node-set. It puts the selected para nodes together into the final node-set and returns a set of pointers to them: "Your nodes are here!".
The initial context does not always have to be the root node of the document. It depends on the environment or application. Whatever application (e.g. database or browser) or specification (e.g. XSLT or XPointer) is using XPath must specify the starting context. In XSLT there is always a concept of the current node. That node is the context node for location paths that appear in XSLT transforms. In XPointer, the starting context is always the root node of the particular document, selected by its URI. In some sort of document database, we might be allowed to do a query across thousands of documents. The root node of each document would become the context node in turn. XPath itself does not have a concept of working with multiple documents but it can be used in a system that does. In addition to the current node, an application could specify some other details of the context: it could supply some values for variables and functions that can be used in the XPath expression. It could also include namespace information that can be used to interpret prefixed names in a location path. 24.2.1.5 Axes But wait. That's not all! Up to now we've always stepped down the tree, to a child element. But we can also step up the tree instead of down and step many levels instead of one. We can step in directions that are neither up nor down but more like sideways. For example we can step from elements to attributes and from attributes to elements. We can also step from an element directly to a child of a child of a child (a descendant). These different ways of stepping are called axes. For example, the descendant axis (abbreviated //) can potentially step down all the levels of the tree. The location path "/mydoc//footnote" would select all footnotes in the current document, no matter how many levels deep they occur. The parent axis uses an abbreviated syntax (..) that is similar to that for going up a directory in a file system. For instance we could select all of the elements containing a footnote like this: "/mydoc//footnote/..". The attribute axis (abbreviated "@") steps into the attribute nodes of an element. The namespace axis is used for namespace information associated with an element node. There are a number of less commonly used axes as well. You can find out more about them in the XPath specification. 24.2.1.6 Node tests The attribute and namespace axes each have only one type of node, which is (necessarily!) its principal node type. The other axes, however, have element as the principal node type but have comment, processing instruction, and text node types as well. We'll refer to such an axis as a content axis and its nodes as content nodes. A step normally selects nodes of the principal type. In the case of content axes, a node test can be used to select another type. For example, the node test text() selects text nodes. 24.2.2 Anatomy of a step We've now seen enough of the basics to take a formal look at the parts of a location step. There are three: -
An axis, which specifies the tree relationship between the context node and the nodes selected by the location step. Our examples so far have used the child axis. -
A node test, which specifies the node type of the nodes selected by the location step. The default type is element, unless the axis is one that can't have element nodes. -
Zero or more predicates, which use arbitrary expressions to further refine the set of nodes selected by the location step. The expressions are full-blown XPath expressions and can include function calls and location paths. In Example 24-2 the first predicate is a location path and the second uses an abbreviation for the position() function. In this tutorial, we've only been using abbreviated forms of the XPath syntax, in which common constructs can often be omitted or expressed more concisely. Example 24-3 shows the unabbreviated form of Example 24-2. Note the addition of explicit axis names (child and attribute) and the position() function call. Example 24-3. Unabbreviated form of Example 24-2 /child::mydoc/child::section[attribute::security="public"] /child::para[position()=7] In the remainder of the chapter, we'll take a closer look at each of the three parts: node tests, axes, and predicates. 24.2.2.1 Node tests Some node tests are useful in all axes; others only in content axes. Node tests for all axes are: * any node of the principal type; i.e., element, attribute, or namespace.[6] [6] The asterisk cannot be used as a prefix ("*ara") or suffix ("ara*") as it is in some regular-expression languages. node() any node of any type Node tests solely for content axes are: text() any text node comment() any comment node processing-instruction() any processing-instruction node, regardless of its target name processing-instruction(target-name) any processing-instruction node with the specified target name Here are some examples of node tests used in a content axis: processing-instruction(cursor) all nodes created from a processing instruction with the target name "cursor" part-nbr all nodes created from an element with the element-type name part-nbr text() all text nodes (contrast below) text all nodes created from an element with the element-type name text * all nodes created from elements, irrespective of the element-type name node() all nodes created from elements (irrespective of the element-type name), contiguous character data, comments or processing instructions (irrespective of the target name) 24.2.2.2 Axes The most important axes are described here. 24.2.2.2.1 Child The default axis is the child axis. That means that if you ask for "/section/para" you are looking for a para in a section. If you ask merely for "para" you are looking for the para element children of the context node, whatever it is. 24.2.2.2.2 Attribute When using the symbol "@" before either an XML name or the node test "*", one is referring to the attribute axis of the context node. The attribute nodes are attached to an element node but the nodes are not ordered. There is no "first" or "third" attribute of an element. Attribute nodes have a string-value that is the attribute value, and a name that is the attribute name. Some examples of abbreviated references to attribute nodes attached to the context node are: @type an attribute node whose name is "type" @* all attributes of the context node, irrespective of the attribute name 24.2.2.2.3 Descendant We can use the double-slash "//" abbreviation in a location path to refer to the descendant axis. This axis includes not only children of the context node, but also all other nodes that are descendants of the context node. This is a very powerful feature. We could combine this with the wildcard node test, for example, to select all elements in a document, other than the document element, no matter how deep they are: "/doc//*". Some examples: /mydoc//part-nbr all element nodes with the element-type name part-nbr that are descendants of the mydoc document element; that is, all of the part-nbr elements in the document /mydoc//@type all attribute nodes named type attached to any descendant element of the mydoc document element; i.e., all of the type attributes in the document /mydoc//* all elements that are descendants of the mydoc document element; i.e., every element in the document except the mydoc element itself /mydoc//comment() all comment nodes that are descendants of the mydoc document element /mydoc//text() all of the text nodes that are descendants of the mydoc document element; i.e., all of the character data in the document! We do not have to start descendant expressions with the document element. If we want to start somewhere farther into the document we can use "//" in any step anywhere in the location path. We could also begin with "//". A location path that starts with "//" is interpreted as starting at the root and searching all descendants of it, including the document element. 24.2.2.2.4 Self The self axis is unique in that it has only one node: the context node. This axis can solve an important problem. For instance in an XSLT transformation we might want to search for all descendants of the current node. If we begin with "//" the address will start at the root. We need a way to refer specifically to the current node. A convenient way to do this is with an abbreviation: a period (.) stands for the context node.[7] [7] This "dot-convention" also comes from the file system metaphor. Unix and Windows use "." to mean the current directory. So ".//footnote" would locate all footnote descendants of the context node. 24.2.2.2.5 Parent The parent axis (..) of a content node selects its parent, as the axis name suggests. For a namespace or attribute node, however, it selects the node's attached element. You could therefore search an entire document for a particular attribute and then find out what element it is attached to: "//@confidential/..". You could go on to find out about the element's parent (and the parent's parent, etc.): "//@confidential/../..". 24.2.2.2.6 Ancestor There is also a way of searching for an ancestor by name, but it does not have an abbreviated syntax. For example, "ancestor::section" would look for the ancestor(s) of the context node that are named "section". This location path locates the titles of sections that contain images: "//image/ancestor::section/title". 24.2.3 Our story so far Here are some examples of location paths using features we have covered so far: item item element nodes that are children of the context node item/para para element nodes that are children of item element nodes that are children of the context node; in other words, those para grandchildren of the context node whose parent is an item //para para element nodes that are descendants of the root node; in other words, all the para element nodes in the entire document //item/para para element nodes that are children of all item element nodes in the entire document //ordered-list//para para element nodes that are descendants of all ordered-list element nodes in the entire document ordered-list//para/@security security attribute nodes attached to all para element nodes that are descendants of all ordered-list element nodes that are children of the context node */@* attribute nodes attached to all element nodes that are children of the context node ../@* attribute nodes attached to the parent or attached node of the context node .//para para element nodes that are descendants of the context node .//comment() comment nodes that are descendants of the context node 24.2.4 Predicates It is often important to filter nodes out of a node-set. We might filter out nodes that lack a particular attribute or subelement. We might filter out all but the first node. This sort of filtering is done in XPath through predicates. A predicate is an expression that is applied to each node. If it evaluates as false, the tested node is filtered out. We'll discuss some common types of predicate expressions, then look at some examples. 24.2.4.1 Expression types 24.2.4.1.1 Node-sets A location path expression can be used as a predicate. It evaluates to true if it selects any nodes at all. It is false if it does not select any nodes. So Example 24-4 would select all paragraphs that have a footnote child. Example 24-4. Using a location path as a predicate //para[footnote] Recall that the evaluation of a step in the path results in a node-set, each member of which is a context node for an evaluation of the next step.[8] [8] In other words, Example 24-4 is really an abbreviation for "//para[./footnote]". One by one, each member of the result node-set, which in this case is every paragraph in the document, would get a chance to be the context node. It would either be selected or filtered out, depending on whether it contained any footnotes. Every paragraph would get its bright shining moment in the sun when it could be ".".[9] [9] Unfortunately, the moment is brief and the price of failure is exclusion from the selection set. A number of predicates can be chained together. Only nodes that pass all of the filters are passed on to the next step in the location path. For example, "//para[footnote][@important]" selects all paragraphs with important attributes and footnote children. Like other location paths, those in predicates can have multiple steps with their own predicates. Consider the complex one in Example 24-5. It looks for sections with author child elements with qualifications child elements that have both professional and affordable attributes. Example 24-5. A complex location path predicate section[author/qualifications[@professional][@affordable]] 24.2.4.1.2 String-values Not all predicates are location path expressions. Sometimes you do not want to test for the existence of some node. You might instead want to test whether an attribute has some particular value. That is different from testing whether the attribute exists or not. Testing an attribute's value is simple: "@type='ordered'" tests whether the context node has a type attribute with value "ordered". In XPath, every node type has a string-value. The value of an element node that is the context node, for example, is the concatenation of the string-values from the expression: ".//text()". In other words, it is all of the character data content anywhere within the element and its descendants. So we can test the data content of a section's title child element with "section[title='Doo-wop']" and both of the sections in Example 24-6 would match. Example 24-6. Matching sections <section><title>Doo-wop</title> ... <section> <section><title>Doo-<emph>wop</emph></title> ... </section> 24.2.4.1.3 Context position There is more to the context in which an expression is evaluated than just the context node. Among the other things is the node's context position, which is returned by a function call: position()=number. In practice, an abbreviation, consisting of the number alone, is invariably used. A number expression is evaluated as true if the number is the same as the context position. Context position can be a tricky concept to grasp because it is, well, context-sensitive. However, it is easy to understand for the most common types of steps. In a step down the child axis (a/b) the context position is the position of the child node in the parent node. So "doc/section[5]" is the fifth section in a doc. In a step down the descendant axis (a//b[5]) it still refers to the position of the child node in its parent node, not its numerical order in the list of matching nodes. XPath also has a function called "last()". We can use it to generate the number for the last node in a context: "a//b[last()]". We can also combine that with some simple arithmetic to get the next-to-last node: "a//b[last()-1]". 24.2.4.2 Predicate examples Here are some examples, using the predicate types that we've discussed: item[3] third item element child of the context node item[@type]/para para element children of item elements that exhibit a type attribute and are children of the context node //list[@type='ordered']/item[1]/para[1] first para element child of the first item element child of any list element that exhibits a type attribute with the string-value "ordered" //ordered-list[item[@type]/para[2]]//para para elements descended from any ordered-list element that has an item child that exhibits a type attribute and has at least two para element children (whew!) This last example is illustrated in Figure 24-3. Figure 24-3. Evaluating multiple steps ![](/books/2/559/1/html/2/images/013142193x/graphics/24fig03.gif) The XPath spec includes numerous other examples of using predicates. XPath is a powerful expression language, including operators and functions that operate on node-sets, numbers, strings, and booleans. |