Reputation: 31
I'm using the below XML
<Root>
<sample>1</sample>
<sample>2</sample>
<sample>3</sample>
<sample>4</sample>
<sample>5</sample>
<sample>6</sample>
</Root>
I want to get the output as given below
<sample>123456</sample>
I'm using the below XSLT to get the above output. But I'm getting the output like this.
<sample>1</sample>
<sample>23456</sample>
<sample>2</sample>
<sample>3456</sample>
<sample>3</sample>
<sample>456</sample>
<sample>4</sample>
<sample>56</sample>
<sample>5</sample>
<sample>6</sample
><sample>6</sample>
<sample></sample>
This is the XSL code I have tried:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="sample">
<presample>
<xsl:value-of select="."/>
<xsl:variable name="code" select="following-sibling::sample" />
<xsl:for-each select="following-sibling::sample">
<xsl:if test="not(preceding-sibling::sample)">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</presample>
</xsl:template>
</xsl:stylesheet>
Please help me in correcting this XSLT to get the desired output mentioned above.
Upvotes: 3
Views: 197
Reputation: 243459
This is a simple application of the Muenchian grouping method -- a significantly more efficient grouping method than using a sibling axis:
<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="kSampleByVal" match="sample" use="."/>
<xsl:template match="sample[1]" priority="2">
<sample>
<xsl:apply-templates select=
"../*[generate-id()=generate-id(key('kSampleByVal',.)[1])]/text()"/>
</sample>
</xsl:template>
<xsl:template match="*/*"/>
</xsl:stylesheet>
When this transformation is applied on the following XML document (the provided one modified to be made more representative):
<Root>
<sample>1</sample>
<sample>2</sample>
<sample>3</sample>
<sample>4</sample>
<sample>5</sample>
<sample>6</sample>
<sample>3</sample>
<sample>4</sample>
<sample>1</sample>
<sample>2</sample>
<sample>3</sample>
</Root>
the wanted, correct result is produced:
<sample>123456</sample>
Do note:
I recommend using the Muenchian grouping method over siblings comparisson grouping in all cases. Muenchian has O(N) time complexity, while the time complexity of siblings-comparisson grouping is quadratical -- O(N^2). I have seen a sibling-comparisson grouping take 40 minutes, while the Muenchian grouping took just 2 seconds.
Upvotes: 0
Reputation: 101652
How's this:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="sample[1]">
<sample>
<xsl:for-each select=". | following-sibling::sample">
<xsl:value-of select="."/>
</xsl:for-each>
</sample>
</xsl:template>
<xsl:template match="sample[position() > 1]" />
</xsl:stylesheet>
I imagine your ultimate goal is a bit more involved than this, so if you can elaborate on that there may be a better general approach.
Not sure if this is what you're going for, but here's a potential more generic approach:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*[text()[normalize-space(.)] and not(name(preceding-sibling::*[1]) = name())]">
<xsl:copy>
<xsl:variable name="list" select=". | following-sibling::*"/>
<xsl:for-each select="$list">
<xsl:variable name="pos" select="position()" />
<xsl:if test="not($list[position() < $pos and name() != name(current())])">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="*[name(preceding-sibling::*[1]) = name()]" />
<xsl:template match="text()" />
</xsl:stylesheet>
When run on this:
<Root>
<sample>1</sample>
<sample>2</sample>
<sample>3</sample>
<sample>4</sample>
<sample>5</sample>
<sample>6</sample>
<child>
<something>4</something>
<something>5</something>
<something>6</something>
<something>7</something>
<somethingelse>a</somethingelse>
<somethingelse>b</somethingelse>
<somethingelse>c</somethingelse>
<somethingelse>d</somethingelse>
<something>8</something>
<something>9</something>
<something>10</something>
</child>
</Root>
Produces this:
<sample>123456</sample>
<something>4567</something>
<somethingelse>abcd</somethingelse>
<something>8910</something>
Upvotes: 2
Reputation: 12154
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="sample[not(preceding-sibling::sample)]">
<sample>
<xsl:for-each select=". | following-sibling::sample">
<xsl:value-of select="."/>
</xsl:for-each>
</sample>
</xsl:template>
<xsl:template match="sample[preceding-sibling::sample]" />
</xsl:stylesheet>
for input XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<sample>1</sample>
<sample>2</sample>
<sample>3</sample>
<sample>4</sample>
<dummy1>a</dummy1>
<dummy2>b</dummy2>
<dummy3>c</dummy3>
<dummy4>d</dummy4>
<sample>5</sample>
<sample>6</sample>
<sample>7</sample>
<sample>8</sample>
</root>
And the output will be:
<?xml version="1.0" encoding="utf-8"?>
<root>
<sample>12345678</sample>
<dummy1>a</dummy1>
<dummy2>b</dummy2>
<dummy3>c</dummy3>
<dummy4>d</dummy4>
</root>
Upvotes: 0