Subject: Re: 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
|