Reputation: 245
I have an XML which looks like this:
<root>
<name>
<elements T="BI" S="1">1</elements>
<elements T="BI" S="2">2</elements>
<elements T="BI" S="3">3</elements>
</name>
<name>
<elements T="BM" S="1">10</elements>
<elements T="BM" S="2">20</elements>
<elements T="BM" S="3">30</elements>
</name>
<name>
<elements T="XX" S="1">001</elements>
<elements T="XX" S="2">002</elements>
<elements T="XX" S="3">003</elements>
</name>
<name>
<elements T="XX" S="1">005</elements>
<elements T="XX" S="2">007</elements>
<elements T="XX" S="3">009</elements>
</name> </root>
and i am trying to use the following XSLT transformation to convert this XML into another XML:
<xsl:for-each select="root">
<name_code_1>
<xsl:for-each select="name">
<xsl:for-each select="element[not(@T=../preceding-sibling::*/element/@T)]">
<xsl:if test="@T = 'XX' and @S = '1'">
<xsl:value-of select="substring(./text(),1,1000)"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</name_code_1>
<name_code_2>
<xsl:for-each select="name">
<xsl:for-each select="element[not(@T=../preceding-sibling::*/element/@T)]">
<xsl:if test="@T = 'XX' and @S = '2'">
<xsl:value-of select="substring(./text(),1,1000)"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</name_code_2>
<name_code_3>
<xsl:for-each select="name">
<xsl:for-each select="element[not(@T=../preceding-sibling::*/element/@T)]">
<xsl:if test="@T = 'XX' and @S = '3'">
<xsl:value-of select="substring(./text(),1,1000)"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</name_code_3>
<name_code_4>
<xsl:for-each select="name">
<xsl:for-each select="element[not(@T=../preceding-sibling::*/element/@T)]">
<xsl:if test="@T = 'XX' and @S = '1'">
<xsl:value-of select="substring(./text(),1,1000)"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</name_code_4>
<name_code_5>
<xsl:for-each select="name">
<xsl:for-each select="element[not(@T=../preceding-sibling::*/element/@T)]">
<xsl:if test="@T = 'XX' and @S = '2'">
<xsl:value-of select="substring(./text(),1,1000)"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</name_code_5>
<name_code_6>
<xsl:for-each select="name">
<xsl:for-each select="element[not(@T=../preceding-sibling::*/element/@T)]">
<xsl:if test="@T = 'XX' and @S = '3'">
<xsl:value-of select="substring(./text(),1,1000)"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</name_code_6> </xsl:for-each>
I want to flatten out the name tags where T-attribute= "XX"and based on S-attribute ="1" or "2" or so on... populate these elements into 6 different XML tags as shown below. The above logic is not working. Can someone please help me by providing some suggestions as to how to address this problem.
Below is the desired final output:
<some_tag>
<name_code_1>001</name_code_1>
<name_code_2>002</name_code_2>
<name_code_3>003</name_code_3>
<name_code_4>005</name_code_4>
<name_code_5>007</name_code_5>
<name_code_6>009</name_code_6> </some_tag>
Thanks Qubiter
Upvotes: 1
Views: 623
Reputation: 136
This approach is using for-each-group and this is the best method if you are using XSLT 2.0.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="root">
<xsl:for-each-group select="name/elements" group-adjacent="if(@T='XX') then 'element' else 'cc'">
<xsl:choose>
<xsl:when test="current-grouping-key() = 'element'">
<some_tag>
<xsl:for-each select="current-group()">
<xsl:variable name="ss" select="position()"/>
<xsl:element name="{concat('name_code_', $ss)}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</some_tag>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Below is output generated by above XSLT.
<?xml version="1.0" encoding="UTF-8"?>
<some_tag>
<name_code_1>001</name_code_1>
<name_code_2>002</name_code_2>
<name_code_3>003</name_code_3>
<name_code_4>005</name_code_4>
<name_code_5>007</name_code_5>
<name_code_6>009</name_code_6>
</some_tag>
Upvotes: 1
Reputation: 29052
One approach is using the position()
function to get the node index from a subset of elements
nodes:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="text()" /> <!-- remove superfluous text() nodes -->
<xsl:template match="/root"> <!-- main match and creation of <some_tag> element -->
<some_tag>
<xsl:apply-templates select="name/elements[@T='XX']" /> <!-- create subset of nodes with attribute @T='XX' -->
</some_tag>
</xsl:template>
<xsl:template match="elements"> <!-- match elements from subset list -->
<xsl:element name="{concat('name_code_',position())}"> <!-- concat position to node-name -->
<xsl:value-of select="." /> <!-- copy value -->
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output is:
<some_tag>
<name_code_1>001</name_code_1>
<name_code_2>002</name_code_2>
<name_code_3>003</name_code_3>
<name_code_4>005</name_code_4>
<name_code_5>007</name_code_5>
<name_code_6>009</name_code_6>
</some_tag>
Upvotes: 2