Grouping dynamically - xslt

I'm trying to group the data together by summing a node adjacently. Example
<root>
<row id="AAA" val="2"/>
<row id="BBB" val="3"/>
<row id="CCC" val="1"/>
<row id="DDD" val="4"/>
<row id="EEE" val="6"/>
<row id="FFF" val="3"/>
<row id="GGG" val="6"/>
<row id="HHH" val="8"/>
<row id="III" val="3"/>
<row id="JJJ" val="4"/>
<row id="KKK" val="2"/>
<row id="LLL" val="1"/>
</root>
Let's say I have a parameter of 10 then, then every time the values sum to 10 or less than 10, they should be grouped together.
And the result should be
<root>
<grouped>
<row id="AAA" val="2"/>
<row id="BBB" val="3"/>
<row id="CCC" val="1"/>
<row id="DDD" val="4"/>
</grouped>
<grouped>
<row id="EEE" val="6"/>
<row id="FFF" val="3"/>
</grouped>
<grouped>
<row id="GGG" val="6"/>
</grouped>
<grouped>
<row id="HHH" val="8"/>
</grouped>
<grouped>
<row id="III" val="3"/>
<row id="JJJ" val="4"/>
<row id="KKK" val="2"/>
<row id="LLL" val="1"/>
</grouped>
</root>
I tried with group-adjacent with sum(current/#val + following-sibling::row/#val le 10) then tried group-by(sum(#val)) but I can see my basic approach is incorrect. Now I'm wondering, is this even possible. So I thought I'd ask the experts!
Thanks!

In XSLT 1 you could use sibling recursion, in XSLT 3 it is easier but a bit verbose to use xsl:iterate:
<xsl:template match="root">
<xsl:copy>
<xsl:iterate select="row">
<xsl:param name="sum" as="xs:integer" select="0"/>
<xsl:param name="group" as="element(row)*" select="()"/>
<xsl:on-completion>
<xsl:if test="$group">
<group>
<xsl:copy-of select="$group"/>
</group>
</xsl:if>
</xsl:on-completion>
<xsl:variable name="current-sum" select="$sum + xs:integer(#val)"/>
<xsl:if test="$current-sum > 10">
<group>
<xsl:copy-of select="$group"/>
</group>
</xsl:if>
<xsl:next-iteration>
<xsl:with-param name="sum" select="if ($current-sum > 10) then xs:integer(#val) else $current-sum"/>
<xsl:with-param name="group" select="if ($current-sum > 10) then . else ($group, .)"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty-development.net/6pS2B6o
As an alternative, you could use an accumulator that sums the #val values and "remembers" when a "group" has been established, then in the grouping you can use group-starting-with to check the accumulator:
<xsl:param name="max" as="xs:integer" select="10"/>
<xsl:mode on-no-match="shallow-copy" use-accumulators="#all"/>
<xsl:output method="xml" indent="yes"/>
<xsl:accumulator name="window" as="item()*" initial-value="()">
<xsl:accumulator-rule match="root" select="(0, true())"/>
<xsl:accumulator-rule match="root/row"
select="let $val := xs:integer(#val),
$sum := $value[1],
$window-start := $value[2],
$current-sum := $sum + $val
return
if ($current-sum gt $max)
then ($val, true())
else ($current-sum, false())"/>
</xsl:accumulator>
<xsl:template match="root">
<xsl:copy>
<xsl:for-each-group select="row" group-starting-with="*[accumulator-before('window')[2]]">
<grouped>
<xsl:apply-templates select="current-group()"/>
</grouped>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty-development.net/6pS2B6o/1
You can even make that streamable (with the help of Michael Kay):
<?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" exclude-result-prefixes="#all" version="3.0">
<xsl:param name="max" as="xs:integer" select="10"/>
<xsl:mode on-no-match="shallow-copy" use-accumulators="#all" streamable="yes"/>
<xsl:output method="xml" indent="yes"/>
<xsl:accumulator name="window" as="item()*" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="root" select="(0, true())"/>
<xsl:accumulator-rule match="root/row"
select="
let $val := xs:integer(#val),
$sum := $value[1],
$window-start := $value[2],
$current-sum := $sum + $val
return
if ($current-sum gt $max)
then
($val, true())
else
($current-sum, false())"
/>
</xsl:accumulator>
<xsl:template match="root">
<xsl:copy>
<xsl:for-each-group select="row"
group-starting-with="*[boolean(accumulator-before('window')[2])]">
<grouped>
<xsl:apply-templates select="current-group()"/>
</grouped>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

The xsl:for-each-group instruction can't handle this requirement.
As well as Martin's suggestions, another 3.0 approach is fold-left:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="max" as="xs:integer" select="10"/>
<xsl:template match="root">
<xsl:copy>
<xsl:variable name="groups" select="
fold-left(row, ([]), function($groups, $next) {
if (sum(head($groups)?*/#val) + $next/#val le $max)
then (array:append(head($groups), $next), tail($groups))
else ([$next], $groups)
}) => reverse()"/>
<xsl:for-each select="$groups">
<grouped>
<xsl:copy-of select="?*"/>
</grouped>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This builds the groups as a sequence of arrays, one array per group, initially in reverse order: the callback function is executed once for each row, and it adds the row to the first (i.e. last) group if the total is within your threshold, otherwise it starts a new group.
(Why in reverse order? Largely because head() and tail() are convenient, and there's no equivalent for getting the last item and "all except the last").

Related

How to partition dates with XSLT

I have a group of dates and I'd like to create partitions with a criterion such as "exactly 7 days apart" For example this is my source xml:
<root>
<entry date="2019-05-12" />
<entry date="2019-05-19" />
<entry date="2019-05-26" />
<entry date="2019-06-16" />
<entry date="2019-06-23" />
</root>
The result should be like this:
<root>
<group>
<val>12.5.</val>
<val>19.5.</val>
<val>26.5.</val>
</group>
<group>
<val>16.6.</val>
<val>23.6.</val>
</group>
</root>
since the first three and the last two dates are all on a Sunday without a gap.
What I have so far is this:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sd="urn:someprefix"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
>
<xsl:output indent="yes"/>
<xsl:template match="root">
<root>
<xsl:copy-of select="sd:partition(distinct-values(for $i in entry/#date return $i cast as xs:date))"/>
</root>
</xsl:template>
<xsl:function name="sd:partition">
<xsl:param name="dates" as="xs:date*"/>
<xsl:for-each-group select="$dates" group-adjacent="format-date(., '[F]')">
<group>
<xsl:for-each select="current-group()">
<val>
<xsl:value-of select="format-date(.,'[D].[M].')"/>
</val>
</xsl:for-each>
</group>
</xsl:for-each-group>
</xsl:function>
</xsl:stylesheet>
Which only generates one group.
How can I ask for the previous element to be 7 days apart? I know of duration (xs:dayTimeDuration('P1D')), but I don't know how to compare it to a previous value.
I use Saxon 9.8 HE.
I think you can also do it using group-adjacent:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="root">
<xsl:copy>
<xsl:for-each-group select="entry/#date/xs:date(.)"
group-adjacent=". - (position() - 1) * xs:dayTimeDuration('P7D')">
<group>
<xsl:apply-templates select="current-group()"/>
</group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match=".[. instance of xs:date]">
<val>{format-date(.,'[D].[M].')}</val>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/ncdD7mM
To do your grouping, you really need to know the difference in days with the previous element, then you can group starting with dates where the difference is not 7 days. So, you can declare a variable where you build up some new XML with the dates and differences, and then use that to group.
Try this function in your XSLT instead.
<xsl:function name="sd:partition">
<xsl:param name="dates" as="xs:date*"/>
<xsl:variable name="datesWithDiff" as="element()*">
<xsl:for-each select="$dates">
<xsl:variable name="pos" select="position()" />
<date diff="{(. - $dates[$pos - 1]) div xs:dayTimeDuration('P1D')}">
<xsl:value-of select="." />
</date>
</xsl:for-each>
</xsl:variable>
<xsl:for-each-group select="$datesWithDiff" group-starting-with="date[#diff = '' or xs:int(#diff) gt 7]">
<group>
<xsl:for-each select="current-group()">
<val>
<xsl:value-of select="format-date(.,'[D].[M].')"/>
</val>
</xsl:for-each>
</group>
</xsl:for-each-group>
</xsl:function>

XSLT Row counter excluding Null and Zero

<Rows>
<Row RowNo="1" Amount="0,0"/>
<Row RowNo="2" Amount="12"/>
<Row RowNo="3" Amount=""/>
<Row RowNo="4" Amount="00"/>
<Row RowNo="5" Amount="33"/>
<Row RowNo="6" Amount="0,00"/>
<Row RowNo="7" Amount="0"/>
<Row RowNo="8" Amount="00,2"/>
</Rows>
These Row's are my input. I want the output to exclude rows where #Amount is '' or 0. And also not count the row for when #Amount = '' and = 0.
Expected result:
<Rows>
<Row RowNo="1" Amount="12"/>
<Row RowNo="2" Amount="33"/>
<Row RowNo="3" Amount="00,2"/>
</Rows>
How can I do this?
With your given input, a specific (based on input) solution in XSLT 1.0 can be as following:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Rows>
<xsl:for-each select="Rows/Row">
<xsl:choose>
<xsl:when test="#Amount != '' and (number(#Amount) * 1) != '0' and translate(#Amount, '0', '') != ','">
<xsl:copy>
<xsl:attribute name="RowNo">
<xsl:value-of select="count(preceding-sibling::*[#Amount != '' and (number(#Amount) * 1) != '0' and translate(#Amount, '0', '') != ',']) + 1" />
</xsl:attribute>
<xsl:for-each select="#*">
<xsl:if test="name() != 'RowNo'">
<xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</Rows>
</xsl:template>
How about something simple, for a change?
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Rows">
<xsl:copy>
<xsl:for-each select="Row[translate(#Amount, ',0', '')]">
<Row RowNo="{position()}" Amount="{#Amount}"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

xslt: how to normalize numerical element

I would like to add an element to existing XML file with normalized value of existing element in the XML.
Any help would be extremely appreciated.
regards
<?xml version="1.0" encoding="UTF-8"?>
<top>
<Results>
<a>no</a>
<b>10</b>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>no</a>
<b>8</b>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>no</a>
<b>6</b>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<c>32</c>
<d>34</d>
</Results>
</top>
In the sample input xml above, I would like to add "b_nom" elements to each of the results where the value is (b)/(minimum of 'b' grouped with a). The expected output is as below
<?xml version="1.0" encoding="UTF-8"?>
<top>
<Results>
<a>no</a>
<b>10</b>
<b_nom>1.66</b_nom>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>no</a>
<b>8</b>
<b_nom>1.33</b_nom>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>no</a>
<b>6</b>
<b_nom>1</b_nom>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<b_nom>1</b_nom>
<c>32</c>
<d>34</d>
</Results>
</top>
I think you want
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="k1" match="Results" use="a"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:call-template name="identity"/>
<xsl:variable name="min">
<xsl:for-each select="key('k1', ../a)">
<xsl:sort select="b" data-type="number"/>
<xsl:if test="position() = 1">
<xsl:value-of select="b"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<b_nom>
<xsl:value-of select="format-number(. div $min, '0.##')"/>
</b_nom>
</xsl:template>
</xsl:stylesheet>
Here is a changed version of the stylesheet that takes a couple of values identifying a group into account:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:data="http:://example.com/data"
exclude-result-prefixes="data">
<data:data xmlns="">
<group>
<key>no</key>
<values>
<value>no</value>
<value>n</value>
<value>0</value>
</values>
</group>
<group>
<key>yes</key>
<values>
<value>yes</value>
<value>y</value>
<value>1</value>
</values>
</group>
</data:data>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="main-input" select="/"/>
<xsl:variable name="groups" select="document('')/xsl:stylesheet/data:data/group"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:call-template name="identity"/>
<xsl:variable name="this" select="."/>
<xsl:variable name="min">
<xsl:for-each select="$main-input//Results[a = $groups/values[value = $this/../a]/value]">
<xsl:sort select="b" data-type="number"/>
<xsl:if test="position() = 1">
<xsl:value-of select="b"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<b_nom>
<xsl:value-of select="format-number(. div $min, '0.##')"/>
</b_nom>
</xsl:template>
</xsl:stylesheet>
That transforms the input
<?xml version="1.0" encoding="UTF-8"?>
<top>
<Results>
<a>no</a>
<b>10</b>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>n</a>
<b>8</b>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>0</a>
<b>6</b>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<c>32</c>
<d>34</d>
</Results>
</top>
into the output
<top>
<Results>
<a>no</a>
<b>10</b>
<b_nom>1.67</b_nom>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>n</a>
<b>8</b>
<b_nom>1.33</b_nom>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>0</a>
<b>6</b>
<b_nom>1</b_nom>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<b_nom>1</b_nom>
<c>32</c>
<d>34</d>
</Results>
</top>

XSLT How to apply recursion to transform from a flat file to a nested tree

I know that there are several other question/answers similar to this, but I haven't read one that addresses my confusion over this transform. I need to move an XML document from this format:
<root>
<row>
<t0>1</t0>
<title>Main Title</title>
</row>
<row>
<t0>2</t0>
<title>Secondary Title</title>
<note>Note</note>
</row>
<row>
<t0>3</t0>
<title>Tertiary Title</title>
</row>
<row>
<t0>3</t0>
<title>Another Title</title>
</row>
<row>
<t0>2</t0>
<title>A Second Secondary Title</title>
<note>Note</note>
</row>
<row>
<t0>3</t0>
<title>Third Level Title</title>
</row>
<row>
<t0>3</t0>
<title>Title at Level Three</title>
</row>
</root>
to this format:
<root>
<header>List</header>
<t01>
<title>Main Title</title>
<t02>
<title>Secondary Title</title>
<note>Note</note>
<t03>
<title>Tertiary Title</title>
</t03>
<t03>
<title>Another Title</title>
</t03>
</t02>
<t02>
<title>A Second Secondary Title</title>
<note>Note</note>
<t03>
<title>Third Level Title</title>
</t03>
<t03>
<title>Title at Level Three</title>
</t03>
</t02>
</t01>
</root>
I'm using XSLT 2.0 and I'm getting hung up on applying for-each-group recursively. Thanks for your time & trouble.
With XSLT 2.0 I would strongly suggest to use the for-each-grouping together with a recursive function or template; below is a sample using a function:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:output indent="yes"/>
<xsl:function name="mf:group" as="element()*">
<xsl:param name="elements" as="element(row)*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$elements" group-starting-with="row[t0 = $level]">
<xsl:element name="t{format-number($level, '00')}">
<xsl:copy-of select="* except t0"/>
<xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
</xsl:element>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="root">
<xsl:copy>
<header>List</header>
<xsl:sequence select="mf:group(row, 1)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Please give this a try:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root">
<root>
<header>List</header>
<xsl:apply-templates select="row[t0 = 1]" />
</root>
</xsl:template>
<xsl:template match="row">
<xsl:variable name="level" select="t0" />
<xsl:element name="{concat('t0', $level)}">
<xsl:apply-templates select="title | note" />
<xsl:variable name="nextPeer"
select="following-sibling::row[t0 <= $level]" />
<xsl:variable name="children"
select="following-sibling::row[t0 = $level + 1][not($nextPeer)
or (count(preceding-sibling::row) < count($nextPeer[1]/preceding-sibling::row))]" />
<xsl:apply-templates select="$children" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Group contiguous siblings of a type

Given siblings, some of which are <row> elements and some not, like this,
<h />
<row id='v' />
<a />
<b />
<row id='w' />
<d />
<row id='x' />
<row id='y' />
<f />
<r />
<row id='z' />
using xslt 1.0, I need to process them in order but to group the non-row ones together as I go, like this,
<notRow>
<h />
</notRow>
<row id='v' />
<notRow>
<a />
<b />
</notRow>
<row id='w' />
<notRow>
<d />
<row id='x' />
<row id='y' />
<notRow>
<f />
<r />
</notRow>
<row id='z' />
The first and last may or may not be <row> elements.
How?
It can be as short and simple as this (no need of calling templates several times, xsl:for-each, xsl:if). Here is the complete transformation:
<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:key name="kFollowing" match="*/*[not(self::row)]"
use="concat(generate-id(..), '+',
generate-id(preceding-sibling::row[1])
)"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template priority="2" match=
"*/*[not(self::row)
and
(preceding-sibling::*[1][self::row]
or not(preceding-sibling::*)
)]">
<notRow>
<xsl:copy-of select=
"key('kFollowing', concat(generate-id(..), '+',
generate-id(preceding-sibling::row[1])
))"/>
</notRow>
</xsl:template>
<xsl:template match="*/*[not(self::row)]"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML (wrapped into a single top element to make it well-formed):
<t>
<h />
<row id='v' />
<a />
<b />
<row id='w' />
<d />
<row id='x' />
<row id='y' />
<f />
<r />
<row id='z' />
</t>
the wanted, correct result is produced:
<t>
<notRow>
<h/>
</notRow>
<row id="v"/>
<notRow>
<a/>
<b/>
</notRow>
<row id="w"/>
<notRow>
<d/>
</notRow>
<row id="x"/>
<row id="y"/>
<notRow>
<f/>
<r/>
</notRow>
<row id="z"/>
</t>
Update:
The OP has expressed an additional requirement that nodes need be processed by matching templates -- not just copied.
This requires only minimal change:
<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:key name="kFollowing" match="*/*[not(self::row)]"
use="concat(generate-id(..), '+',
generate-id(preceding-sibling::row[1])
)"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template priority="2" match=
"*/*[not(self::row)
and
(preceding-sibling::*[1][self::row]
or not(preceding-sibling::*)
)]">
<notRow>
<xsl:apply-templates mode="group" select=
"key('kFollowing', concat(generate-id(..), '+',
generate-id(preceding-sibling::row[1])
))"/>
</notRow>
</xsl:template>
<!-- This template can be replaced with whatever processing needed -->
<xsl:template match="*" mode="group">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="*/*[not(self::row)]"/>
</xsl:stylesheet>
The template that operates in mode "group" should be substituted with template(s) that implement the exact wanted processing. In this case it copies the matched element -- but in the real application any wanted processing would go here.
You might be able to do a trick with a key to group each non-row element by its preceding row (if there is one), or its parent element if not:
<xsl:key name="elementsFollowingRow"
match="*[not(self::row)]"
use="generate-id( (.. | preceding-sibling::row )[last()])" />
and define a named template to put in a notRow if the current element has any associated elements according to the key
<xsl:template name="addNotRow">
<xsl:if test="key('elementsFollowingRow', generate-id())">
<notRow>
<xsl:copy-of select="key('elementsFollowingRow', generate-id())" />
</notRow>
</xsl:if>
</xsl:template>
Then in the template where you're matching the parent element (the one that contains all these row and non-row elements you can do
<xsl:call-template name="addNotRow" />
<xsl:for-each select="row">
<xsl:copy-of select="." />
<xsl:call-template name="addNotRow" />
</xsl:for-each>
The first call-template outside the for-each will deal with any notRow that is required before the first row, and the call inside the for-each will put in any notRow required after the row in question.
This isn't pretty, but it works.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="t">
<xsl:if test="row[1]/preceding-sibling::*">
<notRow>
<xsl:for-each select="row[1]/preceding-sibling::*" >
<xsl:copy />
</xsl:for-each>
</notRow>
</xsl:if>
<xsl:for-each select="row">
<xsl:copy-of select="."/>
<xsl:if test="following-sibling::row[1]/preceding-sibling::*[generate-id(preceding-sibling::row[1])=generate-id(current())]">
<notRow>
<xsl:for-each select="following-sibling::row[1]/preceding-sibling::*[generate-id(preceding-sibling::row[1])=generate-id(current())]">
<xsl:copy />
</xsl:for-each>
</notRow>
</xsl:if>
</xsl:for-each>
<xsl:if test="row[last()]/following-sibling::*">
<notRow>
<xsl:for-each select="row[last()]/following-sibling::*" >
<xsl:copy />
</xsl:for-each>
</notRow>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
On this XML source
<t>
<h />
<i />
<row id='v' />
<a />
<b />
<row id='w' />
<d />
<row id='x' />
<row id='y' />
<f />
<r />
<row id='z' />
<i />
</t>
it returns the correct result:
<notRow>
<h/>
<i/>
</notRow>
<row id="v"/>
<notRow>
<a/>
<b/>
</notRow>
<row id="w"/>
<notRow>
<d/>
</notRow>
<row id="x"/>
<row id="y"/>
<notRow>
<f/>
<r/>
</notRow>
<row id="z"/>
<notRow>
<i/>
</notRow>
but it does seem there should be something simpler.