What if I don't know the ordinal number of the currency element. I just
know that it has a parameter or child element called name or unit which
name is "us" ?
All of the preceding sibling elements named 'currency' that have an
*attribute* (not "parameter" or "child"... those mean different things in
XPath/XSLT) named 'name' or 'unit' with the *value* 'us' can be selected
like this:
preceding-sibling::currency[@name='us' or @unit='us']
The "first" of those, in reverse order, can be obtained by appending [1] to
the end:
preceding-sibling::currency[@name='us' or @unit='us'][1]
Note this is not the same as
preceding-sibling::currency[position()=1 and @name='us' or @unit='us']
which selects the first preceding currency element only if it's name or unit
attribute is 'us'.
It's also not the same as
(preceding-sibling::currency[@name='us' or @unit='us'])[1]
which selects the first node in forward order since it's not filtering the
location step that uses the preceding-sibling axis.
why "rate|preceding-sibling..." ?
What's the "or" for ?
"|" in an XPath expression is a set union operator, not a logical "or".
It is used when you one to merge 2 node-sets.
Think about how in algebra/trig where you have 2 sets: {1,2,3} and {3,4,5}
.... The union of them is {1,2,3,4,5}. Another property of sets is that
they're unordered: {1,2,3,4,5} and {4,5,2,1,3} are the same set. It's much
the same in XPath.
It's worth noting, however, that "|" means union operator in an XPath
expression (e.g. what goes in a 'select' attribute in XSLT), but is a
separator between alternative node-matching "patterns" when used in a
template 'match' attribute in XSLT (so in that case it's more of an "or").
In the case of "rate|preceding-sibling::currency[1]/rate" we wanted the
union of the set identified by "rate" and the set identified by
"preceding-sibling::curency[1]/rate". We could have also used
"rate|preceding::rate" but I prefer to be more specific to avoid picking up
any other 'rate' nodes that might be in the XML.
It may also help to draw the node tree of your source XML, so you can get a
better sense of what the node relationships are.
<currencies>
<currency name="uk">
<rate>3.4567</rate>
</currency>
<currency name="us">
<rate>6.7890</rate>
</currency>
<currency name="eur">
<rate>7.45656</rate>
</currency>
</currencies>
will look like (please make sure you view this in a fixed-width font):
root
|___element 'currencies'
|___text '\n '
|___element 'currency'
| | \___attribute 'name' = 'uk'
| |___text '\n '
| |___element 'rate'
| | |___text '3.4567'
| |___text '\n '
|___text '\n '
|___element 'currency'
| | \___attribute 'name' = 'us'
| |___text '\n '
| |___element 'rate'
| | |___text '6.7890'
| |___text '\n '
|___text '\n '
|___element 'currency'
| | \___attribute 'name' = 'eur'
| |___text '\n '
| |___element 'rate'
| | |___text '7.45656'
| |___text '\n '
|___text '\n'
Also note that the use of the major reverse axes (preceding,
preceding-sibling, ancestor) is hard to optimize at runtime. If you are
doing a lot of these kinds of queries, you are better off using xsl:key to
build some lookup functions to make those node selections. The performance
hit then occurs once at the beginning of stylesheet processing, rather than
each time you need to do the lookup. I will leave it to you to investigate
how xsl:key and key() works.