qubiter
qubiter

Reputation: 245

Advanced XSLT Transformation for XML to XML Conversion

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

Answers (2)

user3681005
user3681005

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

zx485
zx485

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

Related Questions