Re: [xsl] Using an empty xsl:for-each statement to "touch" elements

Subject: Re: [xsl] Using an empty xsl:for-each statement to "touch" elements
From: Michael Kay <mike@xxxxxxxxxxxx>
Date: Thu, 12 Dec 2013 11:01:39 +0000
The way the spec is written (I'll go on to the Saxon implementation later) you
shouldn't need to "touch" the elements.

Looking at the draft here:

http://www.w3.org/TR/2013/WD-xslt-30-20131212/

You should be able to do

>  <xsl:template match="purchase-order">
>        <Total-Cost>
>            <xsl:value-of select="accumulator-after('f:item-cost')"/>
>        </Total-Cost>
>    </xsl:template>


which is the equivalent of

> <xsl:template match="purchase-order">
>        <Total-Cost>
>            <xsl:value-of select="f:total-item-cost()"/>
>        </Total-Cost>
>    </xsl:template>

in the 2012 draft.

The reason this works is that the xsl:value-of qualifies as both a pre-descent
and a post-descent instruction, because it has no consuming instruction as
either a preceding or a following sibling.

I dare say this almost certainly doesn't work in Saxon 9.5, where as you
suggest you will probably have to inject a consuming instruction into the
template before you can get the post-descent value of the accumulator. You
could for example write <xsl:sequence select="nothing"/>.

Michael Kay
Saxonica

On 12 Dec 2013, at 10:40, Costello, Roger L. <costello@xxxxxxxxx> wrote:

> Hi Folks,
>
> Suppose you want to add together all the costs in this purchase order:
>
> <purchase-order>
>    <item>
>        <cost>10</cost>
>    </item>
>    <item>
>        <cost>20</cost>
>    </item>
>    <item>
>        <cost>19</cost>
>    </item>
>    <item>
>        <cost>25</cost>
>    </item>
>    <item>
>        <cost>17</cost>
>    </item>
> </purchase-order>
>
> The new XSLT 3.0 accumulator can be used. The accumulator is updated each
time a <cost> element is "touched". To "touch" each <cost> element I have this
empty loop:
>
> <xsl:for-each select="item">
>    <xsl:for-each select="cost"/>
> </xsl:for-each>
>
> That inner loop doesn't need any statements because its sole purpose is
merely to "touch" the cost element, and thereby trigger the accumulator to be
updated.
>
> Here is the XSLT program with the accumulator:
>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
>                         xmlns:xs="http://www.w3.org/2001/XMLSchema";
>                         exclude-result-prefixes="#all"
>                         version="3.0">
>
>    <xsl:output method="xml" />
>
>    <xsl:accumulator name="f:item-cost"
>                post-descent="f:total-item-cost"
>                as="xs:integer"
>                initial-value="0">
>        <xsl:accumulator-rule match="cost" new-value="$value +
xs:integer(.)"/>
>    </xsl:accumulator>
>
>    <xsl:template match="purchase-order">
>        <Total-Cost>
>            <xsl:for-each select="item">
>                <xsl:for-each select="cost"/>
>            </xsl:for-each>
>            <xsl:value-of select="f:total-item-cost()" />
>        </Total-Cost>
>    </xsl:template>
>
> </xsl:stylesheet>
>
> I realize that this problem could be solved in other ways, but I wanted to
show how it could be solved using an accumulator. And that invariably led me
to an empty xsl:for-each statement.
>
> Is there a better way to "touch" an element than using an empty xsl:for-each
statement?
>
> /Roger

Current Thread