I want to group r
element on the base of its attribute's values:
- If attribute values are same for current and following
r
elements then all should be wrap inside oner
element. - Need to remove all
r
element which is preceding byr
which have same attributes and values. - If empty sequences of same elements present then keep only one.
Problem i am facing is : - its remove last r
you can refer as highlighted. 3 merge with above one
XML:
<text width="12240" height="15840" orient="">
<p style="pxpub">
<r/>
<r/>
</p>
<p type="itemlist" style="TRH2">
<r pub="first"/>
<r/>
<r pub="first">1 </r>
<r pub="first">2some text</r>
</p>
<p type="itemlist" style="TRH2">
<r pub="first"/>
<r pub="first"/>
<r/>
<r pub="a">1 some text</r>
<r pub="a"> 2 merge with above one</r>
<r pub="a"> 3 merge with above one</r>
<r pub="first"/>
<r pub="b"> 4 merge with above one</r>
<r pub="b"> 5 merge with above one</r>
<r pub="second"/>
</p>
XSLT:
<?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="xs"
version="2.0">
<xsl:strip-space elements="*"/>
<xsl:output method="xhtml"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="r">
<xsl:variable name="selfattval" select="string-join(.//@*,'')"/>
<xsl:choose>
<xsl:when test="preceding-sibling::*[1][self::r[string-join(distinct-values(.//@*),'') = $selfattval]]/node()"/>
<xsl:when test="$selfattval = preceding-sibling::*[1][self::r]/string-join(distinct-values(.//@*),'')"/>
<xsl:otherwise>
<r>
<xsl:apply-templates select="@* | node()"/>
<xsl:apply-templates select="following-sibling::*[1][self::r[string-join(distinct-values(.//@*),'') = $selfattval]]/node()"/>
</r>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Current Output:
<text width="12240" height="15840" orient="">
<p style="pxpub">
<r></r>
</p>
<p type="itemlist" style="TRH2">
<r pub="first"></r>
<r></r>
<r pub="first">1 2some text</r>
</p>
<p type="itemlist" style="TRH2">
<r pub="first"></r>
<r></r>
<r pub="a">1 some text 2 merge with above one</r>
<r pub="first"></r>
<r pub="b"> 4 merge with above one 5 merge with above one</r>
<r pub="second"></r>
</p>
</text>
Desire Output:
<text width="12240" height="15840" orient="">
<p style="pxpub">
<r></r>
</p>
<p type="itemlist" style="TRH2">
<r pub="first"></r>
<r></r>
<r pub="first">1 2some text</r>
</p>
<p type="itemlist" style="TRH2">
<r pub="first"></r>
<r></r>
<r pub="a">1 some text 2 merge with above one 3 merge with above one</r>
<r pub="first"></r>
<r pub="b"> 4 merge with above one 5 merge with above one</r>
<r pub="second"></r>
</p>
CodePudding user response:
It sounds like a task for for-each-group group-adjacent
:
<xsl:template match="*[r]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="r" group-adjacent="@*" composite="yes">
<xsl:copy>
<xsl:apply-templates select="@*, current-group()/node()"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
This is XSLT 3 as supported since Saxon 9.8 and Saxon JS 2 but as long as you only have the pub
attribute using
<xsl:template match="*[r]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="r" group-adjacent="@*">
<xsl:copy>
<xsl:apply-templates select="@*, current-group()/node()"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
should do the same in XSLT 2, or if you have multiple attributes and need them all to contribute to the grouping key, string-join
e.g. group-adjacent="string-join(@*, '|')"
them in XSLT 2.