Hi Lars,
Here are my answers to the review questions in Ch. 7 of Tennison's
"Beginning XSLT".
Just a couple of things:
4. Assuming that the prefix tv is associated with the namespace
http://www.example.com/TVGuide within the stylesheet, what kinds of
elements do the following expressions select?
Answer:
Program -- any child element of . named "Program" in no namespace
tv:* -- any child element node of . in the http://www.example.com/TVGuide
namespace
tv:Program -- any child element of . named "Program" in the
http://www.example.com/TVGuide namespace.
*[name() = 'Program'] -- same as just Program
Not quite. When you use the name() function, what you get depends on
the prefix that was used in the source document. If, in the source
document, the http://www.example.com/TVGuide namespace is the default
namespace, as in:
<Program xmlns="http://www.example.com/TVGuide">
...
</Program>
then name() = 'Program' might return true for that element.
I say might because if the namespace is *also* declared with the
prefix 'tv', as in:
<Program xmlns="http://www.example.com/TVGuide"
xmlns:tv="http://www.example.com/TVGuide">
...
</Program>
then the processor is free to return either "Program" or "tv:Program".
It doesn't matter what prefix was actually used on the element, only
what prefixes are declared for the element's namespace.
With that in mind, you might want to rephrase your answers to:
*[starts-with(name(), 'tv:')] -- any child element of . that has
the 'tv:' prefix in the source document (regardless of what namespace
'tv:' may be associated with in either document).
*[name() = 'tv:Program'] -- any child element of . named "Program"
with a 'tv:' prefix in the source document.
5. Add XML comments to the following XSLT extract to explain what it
does:
[snip]
Here's an adjusted version to change the [index] from base zero to
base one; and to display the index only if the element in question
has siblings of the same name (i.e. other than itself):
<!-- Loop through ancestors and . (in doc order) -->
<xsl:for-each select="ancestor-or-self::*">
<!-- set 'name' variable to name of current node, incl. prefix -->
<xsl:variable name="name" select="name()" />
<!-- output '/' and name. -->
/<xsl:value-of select="$name" />
<!-- If this node has siblings of the same name, -->
<xsl:if test="count(../*[name() = $name]) > 1">
<!-- output an index predicate, e.g. [2] if this is the 2nd one. -->
[<xsl:value-of select="count(preceding-sibling::*[name() = $name]) +
1"
/>]
</xsl:if>
</xsl:for-each>
<!-- Result: output the path of the current node. -->
Excellent. In general I'd avoid using count() if you can because naive
processors are likely to collect all the nodes that the path selects
so that they can be counted. If you use:
preceding-sibling::*[name() = $name] or
following-sibling::*[name() = $name]
then a processor can stop looking once it's found the first preceding
or following sibling with the same name (and doesn't even have to look
at the following siblings if it finds a preceding sibling with the
same name).
This probably makes barely any difference to the performance of the
stylesheet unless there are loads and loads of siblings; it's more of
a style thing.
Cheers,
Jeni
---
Jeni Tennison
http://www.jenitennison.com/
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list