XSLT moving items inside other tag at the same level
I have this problem for xslt:
This is the input file:
<root>
<header/>
<item/>
… other n-1 <item/>
<header/>
<item/>
… other m-1 <item/>
</root>
so header and item are at the same level (/root). it must be converted in something like:
<root2>
<header2&开发者_StackOverflow中文版gt;
<item2/>
…<item2/> // the first n-items up
</header2>
<header2>
<item2/>
…<item2/> // the last m-items up
</header2>
</root2>
so basically the first n-item must be moved in the first header while the second group of items must be moved in the second header. Any idea how to get this?
Thanks
Randomize
Sample XML:
<root>
<header/>
<item>1</item>
<item>2</item>
<item>3</item>
<header/>
<item>5</item>
<item>6</item>
<item>7</item>
</root>
XSLT using grouping:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="k" match="item" use="count(preceding-sibling::header)"/>
<xsl:template match="/">
<root2>
<xsl:apply-templates select="root/item[generate-id(.) = generate-id(key('k', count(preceding-sibling::header)))]" mode="a"/>
</root2>
</xsl:template>
<xsl:template match="item" mode="a">
<header2>
<xsl:apply-templates select="key('k', count(preceding-sibling::header))"/>
</header2>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Or simple particular XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<root2>
<header2>
<xsl:apply-templates select="root/item[count(preceding-sibling::header) = 1]"/>
</header2>
<header2>
<xsl:apply-templates select="root/item[count(preceding-sibling::header) = 2]"/>
</header2>
</root2>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Both produce the same output:
<root2>
<header2>
<item>1</item>
<item>2</item>
<item>3</item>
</header2>
<header2>
<item>5</item>
<item>6</item>
<item>7</item>
</header2>
</root2>
This is a simpler more efficient XSLT 1.0 solution, than counting all preceding siblings:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kFollowing" match="item"
use="generate-id(preceding-sibling::header[1])"/>
<xsl:template match="/*">
<root>
<xsl:apply-templates select="header"/>
</root>
</xsl:template>
<xsl:template match="header">
<heather2>
<xsl:copy-of select="key('kFollowing', generate-id())"/>
</heather2>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<root>
<header/>
<item>1</item>
<item>2</item>
<item>3</item>
<header/>
<item>5</item>
<item>6</item>
<item>7</item>
</root>
the wanted, correct result is produced:
<root>
<heather2>
<item>1</item>
<item>2</item>
<item>3</item>
</heather2>
<heather2>
<item>5</item>
<item>6</item>
<item>7</item>
</heather2>
</root>
Explanation:
A key is defined in such a way that for a header
any of the immediately-following item
elements is matched by the generate-id()
of this header
.
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<root>
<xsl:for-each-group select="item"
group-adjacent=
"generate-id(preceding-sibling::header[1])">
<header2>
<xsl:sequence select="current-group()"/>
</header2>
</xsl:for-each-group>
</root>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the same XML document (above), the same correct result is produced:
<root>
<header2>
<item>1</item>
<item>2</item>
<item>3</item>
</header2>
<header2>
<item>5</item>
<item>6</item>
<item>7</item>
</header2>
</root>
Explanation: Use of xsl:for-each-group
, group-adjacent
, current-group()
精彩评论