I am trying to learn more about streamed processing in XSLT 3.0,
currently about streamed accumulators. I have a sample stylesheet which
in the `select` attribute of an `xsl:accumulator` rule only selects the
`@type` attribute of the matched `product` element, but the expression
does so several times:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="xs map"
version="3.0">
<xsl:mode streamable="yes"/>
<xsl:global-context-item use-accumulators="type-count"/>
<xsl:output method="text"/>
<xsl:accumulator name="type-count" as="map(xs:string, xs:integer)"
initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="product">
<xsl:sequence select="if (map:contains($value, @type))
then map:put($value, string(@type),
$value(@type) + 1)
else map:put($value, string(@type), 1)"/>
</xsl:accumulator-rule>
</xsl:accumulator>
<xsl:template match="/">
<xsl:for-each select="*/*">
<xsl:message select="node-name(.)"/>
</xsl:for-each>
<xsl:value-of select="serialize(accumulator-after('type-count'), map{
'method' : 'adaptive'})"/>
</xsl:template>
</xsl:stylesheet>
Saxon 9.7 EE does not compile the stylesheet, indicating
Error at xsl:accumulator-rule on line 15 column 41 of test2016010206.xsl:
XTSE3430: The xsl:accumulator-rule/@select expression for a streaming
accumulator must be
motionless. Operand [@type, ...] of apply(...) selects streamed nodes
in a context that
allows arbitrary navigation (line 18)
Why is the access to an attribute with `string(@type)` not motionless?
When I fix the stylesheet with a `let` expression, as in
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="xs map"
version="3.0">
<xsl:mode streamable="yes"/>
<xsl:global-context-item use-accumulators="type-count"/>
<xsl:output method="text"/>
<xsl:accumulator name="type-count" as="map(xs:string, xs:integer)"
initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="product">
<xsl:sequence select="let $type := string(@type)
return if (map:contains($value, $type))
then map:put($value, $type, $value($type) + 1)
else map:put($value, $type, 1)"/>
</xsl:accumulator-rule>
</xsl:accumulator>
<xsl:template match="/">
<xsl:for-each select="*/*">
<xsl:message select="node-name(.)"/>
</xsl:for-each>
<xsl:value-of select="serialize(accumulator-after('type-count'), map{
'method' : 'adaptive'})"/>
</xsl:template>
</xsl:stylesheet>
the code compiles and runs fine. But I wonder why the multiple use of
the same attribute causes an error, I thought during streaming of an
element its attributes are always accessible.