Why descendant-or-self:: is not allowed in template pattern? - xslt

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.

Related

How can i use xsl:if for this result?

How can i use xsl:if for output type=label. I don't how can I make if statement syntax.
I'm use xslt 1.0.
<xsl:if test="">
<xsl:attribute name="type">
<xsl:value-of select=""/>
</xsl:attribute>
</xsl:if>
this is resource :
<xxxxx type="str">label</xxxxx>
I like to output like this
<key name="xxxxx" type="label"/>
The expression you want is this, assuming you are matching the xxxxx element
<xsl:if test="#type='str'">
Note that, I don't know what the rest of your XSLT looks like, or if you were looking for something generic, but you might want to learn about Attribute Value Templates if you were creating or changing other attributes. For example...
<xsl:template match="*">
<key name="{local-name()}">
<xsl:if test="#type='str'">
<xsl:attribute name="type">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:if>
</key>
</xsl:template>
When applied to this XSLT
<xxxxx type="str">label</xxxxx>
The following is output
<key name="xxxxx" type="label"/>
How can i use xsl:if for output type=label. I don't how can I make if
statement syntax.
I'm use xslt 1.0.
When using XSLT it is rarely necessary to use any XSLT conditional instructions at all -- when using the full power of the language these can (and should) be avoided.
Here is one such solution to the problem:
<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="xxxxx[#type='str']">
<key name="xxxxx" type="{.}"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (none provided!):
<t>
<a/>
<xxxxx type="str">label</xxxxx>
<b/>
<c/>
</t>
the wanted, correct result is produced:
<key name="xxxxx" type="label"/>

Main differences between xsl:copy, xsl:next-match and xsl:copy-of

Please suggest the main differences between these XSLT functions, where results are same for these three functions for the below input and XSLT code (remove comment and execute). Suggest the particular importance of usage of these functions. Are these functions are differed in namespaces area. (XSLT2)
Input xml:
<root>
<a>The text a1
<b>The text b1</b>
<b>The text b2
<c>The text c1</c>
</b>
<c>The text c2</c>
</a>
</root>
XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--xsl:template match="b">
<xsl:element name="B"><xsl:next-match/></xsl:element>
</xsl:template-->
<!--xsl:template match="b">
<xsl:element name="B"><xsl:copy-of select="."/></xsl:element>
</xsl:template-->
<xsl:template match="b">
<xsl:element name="B">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
You currently cannot see a difference between your approaches because your example is still too simple. Let's describe the difference between <xsl:copy-of> and <apply-templates> first: <xsl:copy> makes a literal copy of the context node . which is for example:
<b>The text b2
<c>The text c1</c>
</b>
In the literal copy the XSLT processor does not check anymore if there are any matching rules for the child tag <c>. They are simply ignored.
The tag <xsl:apply-templates> however, applies all available template rules to any given depth, so if you had a rule for <c> it would be applied.
Hence: to see the difference between those first two options create a template match for <c> which does not make a literal copy of it.
Understanding the use of <xslt:next-match> is slightly more difficult. It requires you to know what the next best possible template match would be at the point that you call it. In your case, since you only have the default copy rule
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
next to the specific rule for <b> the default rule would be the next best one. And, of course the default rule does nothing else but copy the sub tree again using the <apply-templates>, so that you do not see any difference.
In order to see a difference there it would be necessary for rule to create a rule for <b> that is less specific than the one present but at the same time more specific that the default rule. This will probably be hard to do.

Xslt - How do you check for a grandchild node with a certain path name. (xpath 1.0)

What I want to do is given an element as context, I want to determine if it has a child with a given name and determine if that child has a node with a given name so I can do operations with it. It is important that I do this in XPath 1.0 syntax.
The code that I've gotten so far is this.
<xsl:for-each select="child::*">
<xsl:if test="contains(name(), 'description')">
<xsl:for-each select="child::*">
<xsl:if test="contains(name(), 'text')">
<xsl:value-of select="node()"/>
</xsl:if>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
It works, but it's big and ugly and I know that there's a way to condense it. The for-eachs there are unnecessary, since I'm only expecting one child node to be named description, and for it to only have one text node.
I feel like this solution should work
<xsl:for-each select="./description/text">
..
</xsl:for-each>
But it isn't, and I'm not really good enough with XPath Syntax to know why.
The reason I'm asking is because though I've found answers that detect whether a child node has a name, and I've found answers that can get to that child node's context, I haven't found an answer that combines the two, though maybe I just haven't been searching hard enough, in which case I apologize.
Edit: Woops, sorry yeah I forgot to mention that the contains() part of the code was also just a hack because I wasn't sure how to compare their values with equality.
Also as long as the answer is there, <xsl:for-each select="description/text"> does not work either.
A sample of the XML in question is this
<leaf>
<description>
<text> Various Words
</text>
</description>
</leaf>
where the context is the leaf and I am trying to get to the text node.
Edit: The Second Coming:
The problem for me was that my XSLT file was using a default namespace (in my case named a). If I had added that then Borodin's answer would have been correct.
To be specific, this is the code which ended up working for me in the end, in case anyone wants to know.
<xsl:for-each select="a:description/a:text>
<xsl:value-of select="node()"/>
</xsl:for-each>
Thanks Guys ^-^
Do you really want to check whether the element names contain those strings? Or, as your narrative says, do you want elements with that exact name?
To do something like what you have already written, use
<xsl:for-each select="*[contains(name(), 'description')]/*[contains(name(), 'text')]">
<xsl:value-of select="node()"/>
</xsl:for-each>
But if you know the complete names it is a lot neater:
<xsl:for-each select="description/text">
<xsl:value-of select="node()"/>
</xsl:for-each>
If that doesn't work then we need to see more of your source XML and your transform.
Update
If I use this XML
<leaf>
<description>
<text>Various Words</text>
</description>
<description>
<text>More Words</text>
</description>
<description>
<text>Other Words</text>
</description>
</leaf>
and apply this stylesheet
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/leaf">
<xsl:for-each select="description/text">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
the output is the expected Various WordsMore WordsOther Words. I don't know how to help you unless you describe your situation better, except to say that transforms should be written with another template rather than for-each wherever possible. Like this variation which produces the same output as above.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/leaf">
<xsl:apply-templates select="description/text"/>
</xsl:template>
<xsl:template match="text">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>

Is daisy chaining xslt an accepted practice?

I have a situation where I think I need to daisy chain my xslt transformation (i.e. that output of one xslt transform being input into another). The first transform is rather complex with lots of xsl:choice and ancestor xpaths. My thought is to transform the xml into xml that can then be easily transformed to html.
My question is 'Is this standard practice or am I missing something?'
Thanks in advance.
Stephen
Performing a chain of transformations is used quite often in XSLT applications, though doing this entirely in XSLT 1.0 requires the use of the vendor-specific xxx:node-set() function. In XSLT 2.0 no such extension is needed as the infamous RTF datatype is eliminated there.
Here is an example (too-simple to be meaningful, but illustrating completely how this is done):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="/*/*"/>
</xsl:variable>
<xsl:variable name="vPass1"
select="ext:node-set($vrtfPass1)"/>
<xsl:apply-templates mode="pass2"
select="$vPass1/*"/>
</xsl:template>
<xsl:template match="num[. mod 2 = 1]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="num" mode="pass2">
<xsl:copy>
<xsl:value-of select=". *2"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
<num>2</num>
<num>6</num>
<num>10</num>
<num>14</num>
<num>18</num>
Explanation:
In the first step the XML document is transformed and the result is defined as the value of the variable $vrtfPass1. This copies only the num elements that have odd value (not even).
The $vrtfPass1 variable, being of type RTF, is not directly usable for XPath expressions so we convert it to a normal tree, using the EXSLT (implemented by most XSLT 1.0 processors) function ext:node-set and defining another variable -- $vPass1 whose value is this tree.
We now perform the second transformation in our chain of transformations -- on the result of the first transformation, that is kept as the value of the variable $vPass1. Not to mess with the first-pass template, we specify that the new processing should be in a named mode, called "pass2". In this mode the value of any num element is multiplied by two.
XSLT 2.0 solution (no RTFs):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vPass1" >
<xsl:apply-templates select="/*/*"/>
</xsl:variable>
<xsl:apply-templates mode="pass2"
select="$vPass1/*"/>
</xsl:template>
<xsl:template match="num[. mod 2 = 1]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="num" mode="pass2">
<xsl:copy>
<xsl:value-of select=". *2"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If this is your situation (or may become your situation):
Transform initial xml to mediary xml.
Maybe transform mediary xml into final1_html.
Maybe transform mediary xml into final2_html (not at all like final1_html).
or
Transform initial xml into mediary xml. This is reasonably likely to change over time.
Transform mediary xml to final_html. This in not likely to change over time.
Then it makes sense to use a two step transformation.
If this is your situation:
Transform initial xml to mediary xml.
Transform mediary xml to final_html.
Then consider not two stepping. Instead just perform one transformation.
I wouldn't think it was standard practice, in particular since you can transform one XML dialect directly to another.
However, if the processing is complex, splitting it to several steps (applying a different transform in each step) can indeed simplify each step and make sense.
It really depends on the particular situation.

XSLT: add node inner text

This is a slightly version of other question posted here:
XSLT: change node inner text
Imagine i use XSLT to transform the document:
<a>
<b/>
<c/>
</a>
into this:
<a>
<b/>
<c/>
Hello world
</a>
In this case i can't use neither the
<xsl:strip-space elements="*"/>
element or the [normalize-space() != ''] predicate since there is no text in the place where i need to put new text. Any ideas? Thanks.
Here is what I would do:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- identity template to copy everything unless otherwise noted -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- match the first text node directly following a <c> element -->
<xsl:template match="text()[preceding-sibling::node()[1][self::c]]">
<!-- ...and change its contents -->
<xsl:text>Hello world</xsl:text>
</xsl:template>
</xsl:stylesheet>
Note that text nodes contain "surrounding" whitespace - in the sample XML in the question the matched text node is whitespace only, which is why the above works. It will stop to work as soon as the input document looks like this:
<a><b/><c/></a>
because here is no text node following <c>. So if this is too brittle for your use case, an alternative would be:
<!-- <c> nodes get a new adjacent text node -->
<xsl:template match="c">
<xsl:copy-of select="." />
<xsl:text>Hello world</xsl:text>
</xsl:template>
<!-- make sure to remove the first text node directly following a <c> node-->
<xsl:template match="text()[preceding-sibling::node()[1][self::c]]" />
In any case, stuff like the above makes clear why intermixing of text nodes and element nodes is best avoided. This is not always possible (see XHTML). But when you have the chance and the XML is supposed to be purely a container for structural data, staying clear of mixed content makes your life easier.
This transformation inserts the desired text (for generality) after the element named a7:
<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()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a7">
<xsl:call-template name="identity"/>
<xsl:text>Hello world</xsl:text>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<a>
<a1/>
<a2/>
.....
<a7/>
<a8/>
</a>
the desired result is produced:
<a>
<a1/>
<a2/>
.....
<a7/>Hello world
<a8/>
</a>
Do note:
The use of the identity rule for copying every node of the source XML document.
The overriding of the identity rule by a specific template that carries out the insertion of the new text.
How the identity rule is both applied (on every node) and called by name (for a specific need).
edit: fixed my fail to put proper syntax in.
<xsl:template match='a'>
<xsl:copy-of select="." />
<xsl:text>Hello World</xsl:text>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>