5.2. The Node-set Core Function Group

There are seven node-set functions. Each of these operates upon the current node-set at the given stage in the evaluation of the expression in which the function is called. Our <year> example is duplicated for convenience in Example 5-1.

Consider the expression count(//harvest//month). In this expression, harvest is the ancestor node from which descendant month elements are to be counted. Notice how we have used a pattern expression comprising the argument to count() function. This is because count() is a node-set function, expecting a node-set as an argument, and pattern expressions return node-sets. The node-set acted upon in this example is the set of <month> elements descended from <harvest>. Because it acts upon a set of nodes, count() is considered a node-set function. What is returned, however, will be a number. The resulting number in this case will be the number of those <month> nodes that are descended from <harvest>, which will yield the number 6.

Example 5-1. Example of our <year>.
<?xml version="1.0"?>
<year>
      <planting>
            <season period="spring">
                  <month>March</month>
                  <month>April</month>
                  <month>May</month>
            </season>
            <season period="summer">
                  <month>June</month>
                  <month>July</month>
                  <month>August</month>
            </season>
      </planting>
      <harvest>
            <season period="fall">
                  <month>September</month>
                  <month>October</month>
                  <month>November</month>
            </season>
            <season period="winter">
                  <month>December</month>
                  <month>January</month>
                  <month>February</month>
            </season>
      </harvest>
</year>

The node-set core function group, unlike the other three function groups, does not contain a function of the same name as its primary core function type. In other words, there is no “node-set()” function. If considered for a moment, this makes sense. A node-set has an implicit structure, each component of which can be of seven possible types, each with a specific syntax and hierarchy. It is not within the scope of a standard such as XSLT or XPath to have a single function that can render a node-set from, for instance, a string or a number. There are far too many subjective options in such a process that preclude making such a function possible—or even viable.

There is, however, one function whose function return type—object returned—is a node-set. The id() function can accept most kinds of objects as an argument and returns a node-set. In spite of the length of its explanation, which concerns how the ID itself is identified, this is a relatively simple function. It has a few limits for use with XSLT processors, however, because it requires the processor to reference the ID attribute declaration in the DTD.

Presented next are three functions that return strings, all of which have optional node-set arguments, local-name(), name(), and namespace-uri(). These three functions work together to access the various parts of namespaced nodes, including the name that comes after the colon (the local name), the name that comes before the colon (the qualified, or QName), and the actual identifier, or Uniform Resource Identifier (URI), respectively. In each case, then, the name or URI returned is of the return type string.

Following those are three functions that return numbers; two with no arguments and a third, which requires a node-set (last(), position(), and count()). These functions are not unlike “inventory” functions for determining quantitatively the numerical values represented in each function name. Thus, the last() function gives the count value of the final node in the node-set, which is, accordingly, the total number of nodes in the node-set being evaluated by the expression. Slightly different is the count() function, which totals all nodes in the node-set referenced by the argument. The position() function simply gives the numerical count of where the node is with respect to its location within the node-set, in document order, based on the expression's evaluation context. No functions in the node-set core function group return or operate on Booleans.

5.2.1. The id() Function

The id() function will select an element based on the unique ID of the element. The id() function operates mainly on node-sets, but will also accept any other object, which it converts to a string prior to processing. The id() function is the only node-set core function that returns a node-set. Therefore, its function return type is that of node-set, and it has a required argument of object, as shown in the following function prototype.

Function: node-set id(object)
Function Name Core Function Group Returns Arguments Argument Type
id() Node-set Node-set Object required

The id() function specifically looks for an element with an attribute of type ID in the given node-set. IDs are a particular type of attribute in XML, which must be declared as such in the DTD. Thus the id() function can only be used with valid XML documents—those conforming to a DTD.

While many XSLT processors have been designed to read attribute definitions and therefore recognize ID attribute content model types, they are not required to do so. If you use id() with a processor that does not read attibute definitions, the id() function will always return an empty result, or no object whatsoever. Note that the specification for XSLT does not require parsing of a document in relation to a DTD, so it should not be considered a fault or bug of the processing software.

Note

You are encouraged to review the XSLT key() function, presented in Chapter 11, as in many cases it can be more advantageous for processing efficiency and wider applicability than the XPath id() function.


To use the id() function, it is necessarily assumed that when calling this function, you know the ID structure in the XML data instance source. In other words, consider the following example:

id('n13-9-63')

This function call will return the single element node with an attribute of type ID that is precisely equal to n13-9-63. It does not matter, in this case, what the element is; the only thing that will be selected for matching is an attribute with a value of n13-9-63. The result will be the node-set of the one node that corresponds to the element with that attribute having that value.

Notice in the example that the object type of the argument for the id() function in this case is a string, denoted by the quotes. If the object type of the argument is anything other than a string, the object is converted to a string prior to processing the id() function. The process essentially takes an object of a given kind—node-set or non-node-set—and turns it into a string order for the processor to find the ID, as follows.

  1. If the id() function's argument is not a node-set, the argument is converted to a string according to the string() function rules (see Section 5.3.1).

  2. If the id() function's argument type is a node-set, the same rules as above apply for producing a string; however, each node in the node-set is processed. Each node in the node-set is converted to a string according to the string() rules, based on the type of the node.

The resulting string is treated as a whitespace-separated string of tokens. Each of these strings is then evaluated until a matching ID value is found.

If an ID is not found, it is not an error, but the function will produce an empty node-set after being evaluated. For instance, in the example above with id('n13-9-63'), if there is no such ID, then the result node-set is empty.

In order to use the id() function properly, the following items must be provided:

  1. An XML source instance with an attribute whose content model type is specified as, and conforms to, type ID

  2. A DTD that defines the attributes as the declared attribute content model type ID

  3. An XSLT processor that is, at the very least, equipped to recognize attribute declarations

  4. A knowledge of the possible IDs and their syntax in the source document such that you have a chance of targeting a match in the id() function call.

5.2.2. The local-name() Function

The local-name() function returns the local part of the expanded name of the first node in the node-set. The function has a string as its function return type, that is, its result object is a string. It has one optional argument, which is a node-set, as shown in the following function prototype. If no argument is supplied, then the current node will be used. This function only operates on node-sets.

Function: string local-name(node-set?)
Function Name Core Function Group Returns Arguments Argument Type
local- name() Node-set String Node-set optional

In practice, this means that when you have a node—for example, an attribute or element—with an expanded namespace, the local-name() function returns the portion of the expanded namespace that follows the colon (:). For example, if iowa:harvest is the expanded namespace, the word harvest is the result of evaluating the function.

This function is particularly useful if you have a source node-set that has a lot of namespaced nodes (nodes with qualified names, or QNames) and the intended output will only have elementtype names from a single namespace. In some cases, it might be preferred that the output data be simplified by removing the namespace prefix (in this case, the iowa:). This function can be used to extract only the local name (in this case, the harvest), in effect removing the namespace prefix, or the portion preceding and including the colon.

The default processing model will return only the first conforming, or matching, node's name for this expression. In other words, you could say the following, and still get the same return:

local-name(../*[@period="winter"] | ../*[@period="spring")]

Here, you are using a predicate with [], and asking by identifying the attribute (@) period whose value is winter to identify the parent (..) of any element (*) with that attribute and value. Even though this actually matches <season period="winter">, the parent of which would be <iowa:harvest> and (using the | operator) <season period="spring">, the parent of which would be <iowa:planting>, the first match in document order is <iowa:harvest>. The return would, then, still be harvest, as that is the local-name() of the first match for the expression, in document order. You could not get more than one without using some of the other XSLT elements.

If there is no colon in the matched node's name string, then the entire name is taken to be the local name, and the entire node's name is returned. For example, there might be a source document with a default namespace declared for a <figure> element, and an additional namespace—such as from the MathML DTD—which is <math:figure>. If you want to have your XPath expression act upon any element of the element-type name figure, regardless of its namespace, you would use the local-name() as part of an equivalence evaluation. For instance:

//*[local-name()='figure']

or

<xsl:value-of select="//*[local-name()='figure']" />

In this expression, which has a node test of any element (represented by the * abbreviation) at any level in the node-tree (represented by the //), using the function for the local name of figure returns figure elements of any type, whether or not the element has a namespace declared.

A few additional considerations are worth noting. While commonly used to select the local-name of an element or attribute node, elements and attributes are not the only possible nodes that can be supplied as the node-set argument to local-name(). In addition to attribute and element nodes, there are comment, namespace, processing-instructions (PIs), root (or document root) nodes, and text nodes. Each, in turn, is treated differently when supplied as the argument to local-name().

If the argument to the local-name() function is the root, there can be no distinction allowing for a local-name. The root is simply the document root; it does not have a name or namespace, so the result is an empty string. Text and comment nodes also return an empty string.

If the node-set referenced by the argument is the namespace itself, then the prefix is returned. If the namespace is the default namespace for the input XML document or data, nothing is returned. If you think about it, this makes more sense than perhaps does the technical description. If the node in the argument is the namespace itself, then the name that is local to that namespace is the namespace prefix itself—in other words, that which comes before the colon.

If the argument is a processing-instruction, the attribute value for the “target” of the processing-instruction, which says what kind of software is supposed to be called by the PI, is returned (see Section 6.7.2 for more information on PIs). For example, in <?Pub Caret?>, the Pub is a signal to a specific publishing software (Arbortext), and Caret is the instruction (Caret is the location last edited by an author in Arbortext). Using local-name() on this PI would return Pub.

Table 5-2 indicates the various objects returned from the local-name() function, in the form of a string, when applied to different types of nodes.

Table 5-2. Summary of object types returned by the local-name()
Node Type Object Returned by local-name()
Attribute QName - Name of the attribute
Comment Empty
Element QName - Name of the element
Namespace Namespace prefix - if defined
Processing-instruction Target
Root Empty
Text Empty

The local-name() function provides the user of XPath with the ability to access the part of a node name after the colon when there is an expanded namespace. This function is part of a trio of functions that includes the name() function and the namespace-uri().

The three functions can be used to access all the various parts of an expanded namespace. There are some qualifications to this general statement; however, it is useful to have this conceptual understanding of local-name() as a frame of reference before proceeding with the name() function.

5.2.3. The name() Function

The name() function returns the QName, or the entire expanded name of any namespaced node. QNames are composed of a namespace prefix, a separator (the :), and a local name, and the name() function returns the entire string.

This function returns a string, so string is also its function return type. The only argument it accepts, which is optional, is a nodeset, as shown in the following function prototype. If no argument is supplied, the default node-set is the current node at that point in the XPath expression. The name() function operates only on node-sets.

Function: string name(node-set?)
Function Name Core Function Group Returns Arguments Argument Type
name() Node-set String Node-set optional

In the previous section we discussed getting figure elements, but in this case we are using the expanded namespace version, applying the name() function. The following expression will return the contents of the first node in the node-set of all elements (*) in any context (//), whose expanded name, or the QName, is math:figure.

//*[name()='math:figure']

or

<xsl:template match="/">
      <xsl:value-of select="//*[name()='math:figure']" />
</xsl:template>

The same thing would be the case with an attribute that has a namespace, the entire QName is returned. If the attribute was html:href to identify the HTML hypertext reference attribute, then name() applied to that attribute node would return html:href. The name() function is useful, then, as a means of analyzing what the various names of different nodes in a document might be, or for performing transformations that preserve the entire QName of a node.

Like the local-name() function, each of the seven kinds of nodes is treated differently when used as an argument to name()(see Table 5-3). For example, text, root, and comment nodes, when furnished as an argument to name(), return an empty string. Namespace nodes return the namespace prefix—the text that comes before the colon. Since the namespace URI is null on all processing-instructions, the return value is the PI's “target,” or the identity of the application being called by the PI when furnished as the argument to name().

Table 5-3. Summary of object types returned by the name() function
Node Type Name() result
Attribute QName - name of the attribute
Comment Empty
Element QName - name of the element
Namespace Namespace prefix - if defined
Processing-instruction Target
Root Empty
Text Empty

5.2.4. The namespace-uri() Function

The namespace-uri() function, together with local-name() and name(), completes the trio of namespace functions that selectively accesses the various components of namespaced nodes. Where local-name() retrieved the portion of the name following the colon, and name() retrieved the entire name, namespace-uri() accesses the URI itself.

The namespace-uri() function return type is a string, as shown in the following function prototype. The string returned is the URI that was declared as the identifier for the namespace—if any—in the namespace declaration. This function accepts an optional node-set argument, and only operates on node-sets.

Function: string namespace-uri(node-set?)
Function Name Core Function Group Returns Arguments Argument Type
namespace-uri String String Node-set optional

It is very important to understand that the namespace-uri() function does not return the prefix of the expanded namespace, which is found prior to the : in an expanded namespace, or QName. The result of this function is the value defined for the namespace. More specifically, the function returns a string equivalent to the attribute value for the namespace declaration of the first node, in document order, of the node-set supplied in the argument.

Note

There is no specific function to access the prefix portion of an expanded namespace, but using creative combinations of these and other functions could provide the value.


If no argument is supplied, then the current node at that point in the evaluation of the XPath expression is the node whose namespaceuri is returned. If there is no declared URI, or if it is the default namespace, an empty string is returned.

The namespace-uri() function will only return a string for an element or attribute node. This makes sense because a text, comment, or PI node does not have a URI, and a namespace prefix does not, itself, have a URI. This may sound contradictory, but the prefix represents a URI; it does not have its own URI per se. It if did, this would in fact be redundant and would amount to a namespace for a namespace! Example 5-2 shows our <year> specific to Iowa.

Example 5-2. The <year> specific to Iowa.
<?xml version="1.0"?>
<year xmlns:iowa="http://www.iowa_climate.org/almanac/">
      <iowa:planting>
            <iowa:season period="spring">
                  <month>March</month>
                  <month>April</month>
                  <month>May</month>
            </iowa:season>
            <iowa:season period="summer">
                  <month>June</month>
                  <month>July</month>
                  <month>August</month>
            </iowa:season>
      </iowa:planting>
      <iowa:harvest>
            <iowa:season period="fall">
                  <month>September</month>
                  <month>October</month>
                  <month>November</month>
            </iowa:season>
            <iowa:season period="winter">
                  <month>December</month>
                  <month>January</month>
                  <month>February</month>
            </iowa:season>
      </iowa:harvest>
</year>

Since our example has declared the iowa namespace for the element <harvest>, the returned value using namespace-uri() would be the attribute value for the xmlns:iowa attribute, or http://www.iowa_climate.org/almanac/. However, you can't reference a namespaced node directly in an XSLT stylesheet, so you have to do a little manipulating to get the value. For example, to match on a <harvest> element, use the following XSLT template rule:

<xsl:template match="//*[local-name() = 'harvest']">
      <xsl:value-of select="namespace-uri()"/>
</xsl:template>

Note

Be aware that even though we have used the http:// URL format for the URI, it is not necessary that it actually point to a specific place on the Web. The URI is intended primarily to provide a method for following up on the source of a particular set of element-type names or attribute names.


If we have more than one namespace, the node-set argument enables us to be more specific, as in Example 5-3.

The result is http://www.us_geological.service.gov/, or, the value for the xmlns:usgeo attribute defined for the usgeo namespace on the <year> element, selected from the <usgeo:planting> element's namespace.

If we change this example slightly, we can take advantage of the default document order processing, using wildcards to get the first child node of <year>, which is <usgeo:planting>:

namespace-uri(//year//*)

In this case, http://www.us_geological.service.gov/ will still be the URI returned. The pattern expression //year//* includes all descendants of <year>, but of these, the first in document order is usgeo:planting, so its URI is the one returned. Notice that pattern expressions can be arguments to functions, provided the function accepts a node-set as its argument.

Example 5-3. Declaration of multiple namespaces.
						INPUT:

<?xml version="1.0"?>
<year xmlns:iowa="http://www.iowa_climate.org/almanac/"
      xmlns:usgeo="http://www.us_geological.service.gov/">
      <usgeo:planting>
            <iowa:season period="spring">
                  <month>March</month>
                  <month>April</month>
                  <month>May</month>
            </iowa:season>
            <iowa:season period="summer">
                  <month>June</month>
                  <month>July</month>
                  <month>August</month>
            </iowa:season>
      </usgeo:planting>
      <usgeo:harvest>
            <iowa:season period="fall">
                  <month>September</month>
                  <month>October</month>
                  <month>November</month>
            </iowa:season>
            <iowa:season period="winter">
                  <month>December</month>
                  <month>January</month>
                  <month>February</month>
            </iowa:season>
      </usgeo:harvest>
</year>

TEMPLATE RULE:

<xsl:template match="//*[local-name() = 'planting']">
      <xsl:value-of select="namespace-uri()"/>
</xsl:template>

5.2.5. The last() Function

The last() function returns the total number of nodes in the context node-set. This number is also equivalent to the context size of the node-set, because the numerical value of the last node for the context node-set is equal to the total number of nodes in the node-set (not including descendants). The last() function is one of three node-counting functions in the node-set core function group, which return numbers. It does not accept an argument and operates only on node-sets, as shown in the following function prototype.

Function: number last()
Function Name Core Function Group Returns Arguments Argument Type
last() Node-set Number None

The last() function returns a number, however, since it doesn't accept arguments, the resulting number can only be used in another context. The function can never really return the number by itself. To actually see the value of the number, use the number() function described in Section 5.5.1.

We can, however, use this function to access the contents of the last node in a node-set:

//harvest//month[last()]

or

<xsl:template match="/">
      <xsl:value-of select="//harvest//month[last()]" />
</xsl:template>

The last() function will give us the total number of months in the first season of the harvest period, which is 3, but the entire XSLT expression with <xsl:value-of> will actually return November, which is the contents of the last <month> in the first <season> element in the <harvest> element.

Recall from Chapter 4 that the context node is the node from which the current portion of an expression is being evaluated. Because the <harvest> element contains two <season> elements, each one is used as the context node for the evaluation of the month[last()] portion of the expression.

The last() function can also be used as a sort of “on/off” testing switch if you are performing a transformation on a number of nodes, but do not want to perform that transformation on the last node.

//harvest//month[position()!=last()]

or

<xsl:template match="/">
      <xsl:value-of select="//harvest//month[position()!=last()]"
      />
</xsl:template>

When used with another function in the node-set core function group, position() and the operator != (not equal to), the function will return the contents of any <month> that was not the last in the set specified by the current context (assuming you are processing each <month> with some sort of looping or iterative mechanism, like the <xsl:for-each> function described in Chapter 9).

Note

Predicates can change the size of the node-set by selecting or eliminating nodes, so the number that is returned by the last() function is the number of the last node of the current nodeset for the given stage in the processing of the XPath expression where the function is called. In other words, as the current context node can change in the course of a predicate's evaluation, so then can the count of the current context of nodes change.


5.2.6. The position() Function

Like the last() function, the position() function returns a number, but since it doesn't accept arguments, the function is only really useful when used in combination with other functions. For example, this function can be used in an equivalence expression like position() = 1, or in XSLT elements like <xsl:value-of> that can pull out the value of the position().

This function has a return type of number and is part of a trio with last() and count(), which together enable a range of inventory and numerical sequence-based operations on node-sets. The position() function does not accept an argument, as shown in the following function prototype. The implicit argument is the current node, which is why this function is classified as a node-set function. It only operates on node-sets.

Function: number position ()
Function Name Core Function Group Returns Arguments Argument Type
position() Node-set Number None

Using the Markup City model, slightly revised in Example 5-4, we can navigate without knowing the street names. Note that it may be necessary to remove any extra space and tabs from this example because some processors consider whitespace objects as “children” and they are counted as text nodes.

If our local expert, at whose mercy we are when asking directions, doesn't know the street names (and we've all had that frustrating occurrence) but does know that the store we're seeking is in the fifth block, it's still possible to find it. So with XPath, choosing the fifth block as we “drive”—or traverse, in proper XPath terminology—the <boulevard> is quite simple:

//boulevard/block[position() = 5]

or

<xsl:template match="/">
      <xsl:value-of select="//boulevard/block[position() = 5]" />
</xsl:template>

This expression, using <xsl:value-of>, returns the text value of the fifth <block> element in the <boulevard>, or Old Chimney Road.

As demonstrated previously, position() is often used together with last(), either with the “not” operator (!=) to exclude the last node, or with equal (=) when the last node is to be used.

//boulevard/block[position() = last()]

or

Example 5-4. Revised Markup City model.
<?xml version="1.0"?>
<main>
      <parkway>
            <thoroughfare>Governor Drive</thoroughfare>
            <thoroughfare name="Whitesburg Drive">
                 <sidestreet>Bob Wallace Avenue</sidestreet>
                 <sidestreet>Woodridge Street</sidestreet>
            </thoroughfare>
            <thoroughfare name="Bankhead">
                 <sidestreet>Tollgate Road</sidestreet>
                 <sidestreet>Oak Drive</sidestreet>
            </thoroughfare>
      </parkway>
      <boulevard>
            <block>Panorama Street</block>
            <block>Highland Plaza</block>
            <block>Hutchens Avenue</block>
            <block>Wildwood Drive</block>
            <block>Old Chimney Road</block>
            <block>Carrol Circle</block>
      </boulevard>
</main>

<xsl:template match="/">
     <xsl:value-of select="//boulevard/block[position() =
        last()]" />
</xsl:template>

The result of this expression would be the content of the last <block> element in the <boulevard>, or Carrol Circle.

5.2.6.1. The position() Function with Regard to Context

Because nodes are counted in context, the numbering for position() resets at the context element. Context is not determined by the position() function itself, but by the XSLT element in which the position() function is used. Most XSLT elements get their context from the <xsl:template> element (see Chapter 3, Section 3.1.2 for more on the <xsl:template> element).

When matching beginning from the root, the context is the root, and position() starts at the first element with 1, and sequentially counts elements to the end, as shown in Example 5-5.

Example 5-5. Using position() in an iteration.
<xsl:template match="/">
      <xsl:for-each select="//*">
            <xsl:value-of select="position()"/>
      </xsl:for-each>
</xsl:template>

The result of this template using our Markup City from Example 5-4 is a count of all elements starting at 1 and ending at 16: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16.

Matching from the root, the context for the following template is the root, so the position of the sidestreets is counted sequentially without regard to direct parentage:

<xsl:template match="/">
      <xsl:for-each select="//sidestreet">
           <xsl:value-of select="position()"/>
      </xsl:for-each>
</xsl:template>

The result of this template is a sequential count of the sidestreets starting at 1, regardless of their parent: 1 2 3 4.

Changing the template to match on any sidestreet, the context is changed to the parent element of sidestreet (recall that even though the // is used to denote any sidestreet, the context of the sidestreet is still its parent node):

<xsl:template match="//sidestreet">
      <xsl:value-of select="position()"/>
</xsl:template>

The result of this template is the count of each sidestreet in context of its parent, or 1 2 1 2. The processor takes each element as the context element, starting from the root (//), and looks for sidestreets within that element, first resetting the position() count to 1. Note that if you have any whitespace in your input file, it may be counted as a child of <thoroughfare> and cause your resulting numbers to be off.

5.2.7. The count() Function

The count() function simply counts the nodes—not including their descendants—in the node-set specified in the argument. This function is the third component of the node sequencing and numerical inventory trio of functions. Of the three—count(), last(), and position()—only count() has an argument, which is required, and must be a node-set. The count() function operates on node-sets and returns a number, as shown in the following function prototype.

Function: number count(node-set)
Function Name Core Function Group Returns Arguments Argument Type
count() Node-set Number Node-set required

If we wanted to know how many blocks were in the entire Markup City, we would use count() as shown in Example 5-6.

Example 5-6. Using count() to count blocks.
count(//block)
or

<xsl:template match="/">
      <xsl:value-of select="count(//block)" />
</xsl:template>

This would give us the total number of blocks, in this case 6, regardless their parents. Of course, in our current city, we have only <block>s along the <boulevard>. We could use the union operator | with the count() function to find the total number of either <block>s or <sidestreet>s.

count(//block | //sidestreet)

or

<xsl:template match="/">
      <xsl:value-of select="count(//block | //sidestreet)" />
</xsl:template>

This expression would result in 10, the total of 6 <block>s and 4 <sidestreet>s. Working with more complex expressions within the argument, we could add a predicate that limits which <sidestreet>s we count.

count(//block | //*[@name='Bankhead']/sidestreet)

or

<xsl:template match="/">
      <xsl:value-of select="count(//block |
      //*[@name='Bankhead']/sidestreet)" />
</xsl:template>

This counts all <block>s, regardless from which node they are descended (//), which is equal to 6, and it also counts all <sidestreet>s whose parent (the / preceding sidestreet steps up to the parent of <sidestreet>) is any element (*) with the attribute (@) name with the value Bankhead, which results in 2, for an expression total of 8. If there was a kind of street other than <thoroughfare> with a name attribute with the value Bankhead, its <sidestreet> children would be counted too. To be more specific, but with the same result in this case, we could do the following:

count(//block | //thoroughfare[@name='Bankhead']/sidestreet)

or

<xsl:template match="/">
      <xsl:value-of select="count(//block |
      //thoroughfare[@name='Bankhead']/sidestreet)" />
</xsl:template>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.35.77