Suppose I have the following XML (which is the embedding of TEI annotation scheme into HTML):
<p>(See, for example, <bibl type="journal" xmlns="http://www.tei-c.org/ns/1.0"><author>Greger IH, et al.</author> <date>2007</date>, <title>Trends Neurosci.</title> <biblScope type="vol">30</biblScope> (<biblScope type="issue">8</biblScope>): <biblScope type="pp">407-16</biblScope></bibl>).</p>
Now I want to copy all annotation nodes as is into resulting XHTML but only rename <title>
to <bibTitle>
(as <title>
is only allowed in <head>
), so I used the following transformation:
<xsl:template match="tei:bibl/descendant-or-self::*">
<xsl:variable name="nodeName">
<xsl:choose>
<xsl:when test="name() = 'title'">bibTitle</xsl:when>
<xsl:otherwise><xsl:value-of select="name()" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Changing of the namespace occurs here, but we don't care -->
<xsl:element name="{$nodeName}">
<xsl:copy-of select="@*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="p/text()|tei:bibl//text()">
<xsl:copy-of select="." />
</xsl:template>
However it does not compile and breaks with following error:
Only child:: and attribute:: axes are allowed in match patterns! Offending axes = descendant-or-self
When I change the match rule to <xsl:template match="tei:bibl|tei:bibl//*">
it starts working as intended. But that should be identical to descendant-or-self::*
, right? Have I hit the transformer implementation limitation here?
First I've tested with Mozilla 3.5 internal transformer, then with Xalan 2.7.1 – same negative result.
This limitation is valid only for any location step within the template's match pattern. It is by design (mandated by the W3C XSLT 1.0 and XSLT 2.0 specifications) -- to ensure efficient XSLT processing.
Do note: One can freely use any axis (including descending-or-self::
) withinin the predicates that follow any location step.
Update:
Here is a short, complete example of using the descendant-or-self::
axis in the match
attribute of xsl:template
:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="num[descendant-or-self::num > 5]"/>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<nums>
<num>1</num>
<num>2</num>
<num>3</num>
<num>4</num>
<num>5</num>
<num>6</num>
<num>7</num>
<num>8</num>
<num>9</num>
<num>10</num>
</nums>
the wanted result: any num
elements with value >= 5 are deleted:
<nums>
<num>1</num>
<num>2</num>
<num>3</num>
<num>4</num>
<num>5</num>
</nums>
It's a hard requirement by the spec:
Although patterns must not use the descendant-or-self axis, patterns may use the // operator as well as the / operator.
Here's a way you can rewrite the pattern into an equivalent one that doesn't repeat mention of tei:bibl
:
<xsl:template match="*[ancestor-or-self::tei:bibl]">
As to why the limitation is there, the general answer, yes, is for performance. Perhaps the limitations are overly conservative, because, as you pointed out, the rewrite of descendant-or-self in this case is trivial.
I regularly get annoyed by this limitation (that you can use // but not descendant).
Here is a case where it is not enough:
<a>
<b>
<c>
<c>
<c/>
</c>
</c>
</b>
<c>
<c>
<c/>
</c>
</c>
</a>
now I want to match only:
*[self::a or self::b][p(.)]/c/descendent-or-self::c
i.e., if the predicate p(.) is true on a, I want a/c, a/c/c, a/c/c/c and if it is true on b, I want b/c, b/c/c, and b/c/c/c.
But I do not want a/b/c, a/b/c/c, etc. just because the predicate matches on a and not on b.
If I make a match pattern:
*[self::a or self::b][p(.)]//c
then I match all of them which I do not want.
So I have to do it backwards in the bracket:
c[ancestor-or-self::c/parent::*[self::a or self::b][p(.)]]
I think I just convinced myself that this restriction isn't really a logical restriction, however, I think the excuse not to allow proper axis steps in match patterns is pretty lame, because when I need this, I need this, who cares if it is not as fast as if I use simpler expressions.