Reputation: 1020
I have xml like follows,
<doc>
<section>
<p id="main">aa</p>
<p id="main">bb</p>
<p id="main">cc</p>
<p id="para1">dd</p>
<p id="st_main">ee</p>
<p id="st_main">ff</p>
<p id="xy_main">cc</p>
<p id="xy_main">cc</p>
<p id="main">gg</p>
<p id="para2">hh</p>
<p id="main">ii</p>
<p id="st_main">jj</p>
<p id="xy_main">cc</p>
<p id="main">cc</p>
<p id="para1">xx</p>
<p id="main">yy</p>
<p id="xy_main">zz</p>
<p id="main">cc</p>
</section>
</doc>
My requirements are
1) grouping <p>
by para attribute and add separate section to each <p>
group.
2) identify <p>
node groups which id attribute starting from st
put <st_start>
and <st_end>
at the start and the end on the group
3) identify <p>
node groups which id attribute starting from xy
put <xy_start>
and <xy_end>
at the start and the end on the group
SO the expected output is,
<doc>
<section>
<p id="main">aa</p>
<p id="main">bb</p>
<p id="main">cc</p>
</section>
<section type="para1">
<p id="para1">dd</p>
<ss_start/>
<p id="st_main">ee</p>
<p id="st_main">ff</p>
<ss_end/>
<xy_start/>
<p id="xy_main">cc</p>
<p id="xy_main">cc</p>
<xy_end/>
<p id="main">gg</p>
</section>
<section type="para2">
<p id="para2">hh</p>
<p id="main">ii</p>
<ss_start/>
<p id="st_main">jj</p>
<ss_end/>
<xy_start/>
<p id="xy_main">cc</p>
<xy_end/>
<p id="main">cc</p>
</section>
<section type="para1">
<p id="para1">xx</p>
<p id="main">yy</p>
<xy_start/>
<p id="xy_main">zz</p>
<xy_end/>
<p id="main">cc</p>
</section>
</doc>
I have following xsl to to this task
<xsl:template match="section">
<xsl:for-each-group select="p" group-starting-with="p[starts-with(@id, 'para')]">
<section>
<xsl:for-each-group select="current-group()" group-adjacent="starts-with(@id, 'st')">
<xsl:if test="current-grouping-key()">
<ss_start/>
</xsl:if>
<xsl:apply-templates select="current-group()"/>
<xsl:if test="current-grouping-key()">
<ss_end/>
</xsl:if>
</xsl:for-each-group>
<xsl:for-each-group select="current-group()" group-adjacent="starts-with(@id, 'xy')">
<xsl:if test="current-grouping-key()">
<xy_start/>
</xsl:if>
<xsl:apply-templates select="current-group()"/>
<xsl:if test="current-grouping-key()">
<xy_end/>
</xsl:if>
</xsl:for-each-group>
</section>
</xsl:for-each-group>
</xsl:template>
But is doubled up the content ? How can I modify my code to get the expected output?
Upvotes: 0
Views: 339
Reputation: 70598
I think you only need one nested xsl:for-each-group
here, when you group adjacent nodes by their id
attribute. You can then test by grouping key being equal to st_main
or xy_main
, and if so add the extra nodes.
Try this template
<xsl:template match="section">
<xsl:for-each-group select="p" group-starting-with="p[starts-with(@id, 'para')]">
<section>
<xsl:for-each-group select="current-group()" group-adjacent="@id">
<xsl:if test="current-grouping-key() = 'st_main'">
<ss_start/>
</xsl:if>
<xsl:if test="current-grouping-key() = 'xy_main'">
<xy_start/>
</xsl:if>
<xsl:apply-templates select="current-group()"/>
<xsl:if test="current-grouping-key() = 'st_main'">
<ss_end/>
</xsl:if>
<xsl:if test="current-grouping-key() = 'xy_main'">
<xy_end/>
</xsl:if>
</xsl:for-each-group>
</section>
</xsl:for-each-group>
</xsl:template>
Alternatively, to avoid too much hard-coding, you could just check for the id
attribute ending in "_main" and create dynamic element names. Try this too:
<xsl:template match="section">
<xsl:for-each-group select="p" group-starting-with="p[starts-with(@id, 'para')]">
<section>
<xsl:for-each-group select="current-group()" group-adjacent="@id">
<xsl:if test="ends-with(current-grouping-key(), '_main')">
<xsl:element name="{substring-before(current-grouping-key(), '_')}_start" />
</xsl:if>
<xsl:apply-templates select="current-group()"/>
<xsl:if test="ends-with(current-grouping-key(), '_main')">
<xsl:element name="{substring-before(current-grouping-key(), '_')}_end" />
</xsl:if>
</xsl:for-each-group>
</section>
</xsl:for-each-group>
</xsl:template>
Upvotes: 1