[Top] [All Lists]

[xsl] Can fold-left process the attributes nodes of a streamed element?

2021-07-27 09:02:33

I am struggling to find a compact way to use XSLT 3 streaming and an
accumulator to sum up the attribute values of certain elements,e.g.
xsl:accumulator name="attribute-sums" as="map(xs:QName, xs:decimal)"
initial-value="map{}" streamable="yes" to accumulate the sum of each
attribute by the name of the attribute.

One problem is that accumulators don't fire on attribute nodes, only on
element nodes, therefore I have to write the accumulator-rule to match
on the parent element of the attributes and compute all the sums in a
single rule;

  fold-left(@*, $value, function($v, $a) { map:put($v, node-name($a),
(map:get($v, node-name($a)), 0)[1] + xs:decimal($a)) })

seemed like a compact and elegant use of XPath 3.1 but using a dynamic
inline function as the argument to fold-left doesn't seem to pass the
streamability analysis.

Then I tried to set up a user-defined function to be passed in as the
third argument to fold-left, but somehow that approach doesn't seem to
work either as a streamable stylesheet function needs to take the
streamable nodes as the first parameter while the signature of the
function $f of fold-left would make the attribute node the second parameter.

So the only options I could think of is not passing the attribute nodes
themselves to fold-left but rather an array or map of the name and value
I need to store in the accumulator e.g.

fold-left(@* ! [node-name(), xs:decimal(.)], $value, function($v, $anv)
{ map:put($v, $anv?1, (map:get($v, $anv?1), 0)[1] + $anv?2) })

or to abandon fold-left and use xsl:iterate in the accumulator rules

   <xsl:accumulator name="attribute-sums" as="map(xs:QName,
xs:decimal)" initial-value="map{}" streamable="yes">
        <xsl:accumulator-rule match="Book">
            <xsl:iterate select="@*">
                <xsl:param name="accumulator-map" select="$value"/>
                <xsl:on-completion select="$accumulator-map"/>
                    <xsl:with-param name="accumulator-map"
                        select="map:put($accumulator-map, node-name(),
(map:get($accumulator-map, node-name()), 0)[1] + xs:decimal(.))"/>

The xsl:iterate approach feels too verbose, I kind of think fold-left
should suffice to process the sequence of attributes (without that
escape hatch to map them first to an array or map).

Any ideas or opinions whether I have missed an approach to use fold-left?

XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com

<Prev in Thread] Current Thread [Next in Thread>
  • [xsl] Can fold-left process the attributes nodes of a streamed element?, Martin Honnen martin(_dot_)honnen(_at_)gmx(_dot_)de <=