Search code examples
xmlxsltxsdxslt-1.0xslt-2.0

xsl table row value do not show or show based on eah other year values


I will be having xml as below:

<catalog>
    <performanceVO>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/1998">1998-12-31</rtnDate>
            <rtnWOSC alt="10.73">10.73</rtnWOSC>
            <rtnWSC alt="4.35">4.35</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/1999">1999-12-31</rtnDate>
            <rtnWOSC alt="97.91">97.91</rtnWOSC>
            <rtnWSC alt="86.54">86.54</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2000">2000-12-31</rtnDate>
            <rtnWOSC alt="46.58">46.58</rtnWOSC>
            <rtnWSC alt="38.15">38.15</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2001">2001-12-31</rtnDate>
            <rtnWOSC alt="-20.48">-20.48</rtnWOSC>
            <rtnWSC alt="-25.05">-25.05</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2002">2002-12-31</rtnDate>
            <rtnWOSC alt="-42.48">-42.48</rtnWOSC>
            <rtnWSC alt="-45.79">-45.79</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2003">2003-12-31</rtnDate>
            <rtnWOSC alt="43.55">43.55</rtnWOSC>
            <rtnWSC alt="35.28">35.28</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2004">2004-12-31</rtnDate>
            <rtnWOSC alt="11.96">11.96</rtnWOSC>
            <rtnWSC alt="5.53">5.53</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2005">2005-12-31</rtnDate>
            <rtnWOSC alt="5.55">5.55</rtnWOSC>
            <rtnWSC alt="-0.51">-0.51</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2006">2006-12-31</rtnDate>
            <rtnWOSC alt="5.03">5.03</rtnWOSC>
            <rtnWSC alt="-1.02">-1.02</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2007">2007-12-31</rtnDate>
            <rtnWOSC alt="-1.60">-1.6</rtnWOSC>
            <rtnWSC alt="-7.26">-7.26</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2008">2008-12-31</rtnDate>
            <rtnWOSC alt="-10.64">-10.64</rtnWOSC>
            <rtnWSC alt="-15.78">-15.78</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2009">2009-12-31</rtnDate>
            <rtnWOSC alt="11.83">11.83</rtnWOSC>
            <rtnWSC alt="5.41">5.41</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2010">2010-12-31</rtnDate>
            <rtnWOSC alt="17.28">17.28</rtnWOSC>
            <rtnWSC alt="10.54">10.54</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2011">2011-12-31</rtnDate>
            <rtnWOSC alt="8.35">8.35</rtnWOSC>
            <rtnWSC alt="2.12">2.12</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2012">2012-12-31</rtnDate>
            <rtnWOSC alt="29.35">29.35</rtnWOSC>
            <rtnWSC alt="21.90">21.9</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2013">2013-12-31</rtnDate>
            <rtnWOSC alt="68.64">68.64</rtnWOSC>
            <rtnWSC alt="58.95">58.95</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2014">2014-12-31</rtnDate>
            <rtnWOSC alt="33.91">33.91</rtnWOSC>
            <rtnWSC alt="26.22">26.22</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2015">2015-12-31</rtnDate>
            <rtnWOSC alt="5.50">5.5</rtnWOSC>
            <rtnWSC alt="-0.57">-0.57</rtnWSC>
        </fAnReVal>
        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2016">2016-12-31</rtnDate>
            <rtnWOSC alt="-17.16">-17.16</rtnWOSC>
            <rtnWSC alt="-21.92">-21.92</rtnWSC>
        </fAnReVal>
        <bmCalYearDet>
               <benchMarkOrder>1</benchMarkOrder>
               <bmCalDet>
                  <calYear>2016</calYear>
                  <val alt="11.96">11.96</val>
               </bmCalDet>
               <bmCalDet>
                  <calYear>2015</calYear>
                  <val alt="1.38">1.38</val>
               </bmCalDet>
               <bmCalDet>
                  <calYear>2014</calYear>
                  <val alt="13.69">13.69</val>
               </bmCalDet>
               <bmCalDet>
                  <calYear>2013</calYear>
                  <val alt="32.39">32.39</val>
               </bmCalDet>
               <bmCalDet>
                  <calYear>2012</calYear>
                  <val alt="16.00">16</val>
               </bmCalDet>
               <bmCalDet>
                  <calYear>2011</calYear>
                  <val alt="2.11">2.11</val>
               </bmCalDet>
               <bmCalDet>
                  <calYear>2010</calYear>
                  <val alt="15.06">15.06</val>
               </bmCalDet>
               <bmCalDet>
                  <calYear>2009</calYear>
                  <val alt="26.46">26.46</val>
               </bmCalDet>
               <bmCalDet>
                  <calYear>2008</calYear>
                  <val alt="-37.00">-37</val>
               </bmCalDet>
               <bmCalDet>
                  <calYear>2007</calYear>
                  <val alt="5.49">5.49</val>
               </bmCalDet>
            </bmCalYearDet>
            <bmCalYearDet>
            <benchMarkOrder>2</benchMarkOrder>
            <bmCalDet>
                <calYear>2016</calYear>
                <val alt="-21.68">-21.68</val>
            </bmCalDet>
            <bmCalDet>
                <calYear>2015</calYear>
                <val alt="11.42">11.42</val>
            </bmCalDet>
            <bmCalDet>
                <calYear>2014</calYear>
                <val alt="34.32">34.32</val>
            </bmCalDet>
            <bmCalDet>
                <calYear>2013</calYear>
                <val alt="65.97">65.97</val>
            </bmCalDet>
            <bmCalDet>
                <calYear>2012</calYear>
                <val alt="32.29">32.29</val>
            </bmCalDet>
            <bmCalDet>
                <calYear>2011</calYear>
                <val alt="12.08">12.08</val>
            </bmCalDet>
            <bmCalDet>
                <calYear>2010</calYear>
                <val alt="15.25">15.25</val>
            </bmCalDet>
            <bmCalDet>
                <calYear>2009</calYear>
                <val alt="15.84">15.84</val>
            </bmCalDet>
            <bmCalDet>
                <calYear>2008</calYear>
                <val alt="-12.56">-12.56</val>
            </bmCalDet>
            <bmCalDet>
                <calYear>2007</calYear>
                <val alt="4.64">4.64</val>
            </bmCalDet>
        </bmCalYearDet>
    </performanceVO>
</catalog>

Currently I have issue with my existing logic when there is no particular year node tree from the xml then the values are getting wrongly mapped.

Tabular data:

               2016    2015   2014  2013   2012  2011   2010   2009   2008  2007
rtnWOSC/@alt  -17.16   5.5   33.91  68.64  29.35 8.35   17.28  11.83 -10.64 -1.6
rtnWSC/@alt   -21.92  -0.57  26.22  58.95  21.9  2.12   10.54  5.41  -15.78 -7.26
val/@alt      11.96   1.38   13.69  32.39  16    2.11   15.06  26.46 -37    5.49

When there is no below node (latest year) for example then the values are getting one td push to left and shows wrongly data of 2015 under 2016

<bmCalDet>
      <calYear>2016</calYear>
      <val alt="-21.68">-21.68</val>
</bmCalDet>

Below is my XSL:

<xsl:variable name="performanceCount" select="count(catalog/performanceVO/fAnReVal)"/>    
<thead>
    <tr>
        <td></td>
        <xsl:for-each select="catalog/performanceVO/fAnReVal">
            <xsl:sort data-type="text" select="rtnDate/@alt" order="descending" />

            <xsl:if test="position() &lt; 11">
            <th>
                <xsl:value-of select="substring(rtnDate/@alt, 7, 4)" />
            </th>
            </xsl:if>
        </xsl:for-each>
    </tr>
</thead>
<tbody>
<tr>
    <xsl:for-each select="catalog/performanceVO/fAnReVal">
    <xsl:sort data-type="text" select="rtnDate/@alt" order="descending" />
    <xsl:if test="position() &lt; 11">
    <td class="text-right">
        <xsl:value-of select="$rtnWOSCValue"/>
    </td>
    </xsl:if>
    </xsl:for-each>
</tr>

<tr>
<xsl:for-each select="catalog/performanceVO/bmCalYearDet">
<xsl:sort data-type="text" select="calYear" order="descending" />
        <xsl:variable name="calValue" select="val/@alt" />
        <xsl:if test="position() &lt;= $performanceCount">
            <td class="text-right">
                <xsl:sequence select="frk:dash-if-empty($calValue)" />
            </td>

</xsl:for-each>
</tr>
</tbody>

It is ok to show as empty value when there is no node available. Any suggestions/pointers?

I have tried your below approach for opposite requirment, under W3schools(https://www.w3schools.com/xml/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog) it did not show headings I was able to get variable value

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="bmCalDet-by-year" match="bmCalDet" use="calYear" />
<xsl:template match="/catalog">
    <xsl:variable name="years">
        <xsl:for-each select="performanceVO/fAnReVal">
            <xsl:sort select="rtnDate" order="descending"/>
            <xsl:if test="not(position() > 11)">
                <xsl:copy-of select="."/>
            </xsl:if>
        </xsl:for-each> 
    </xsl:variable> 
<h6>Years: <xsl:value-of select="$years" /></h6>
    <table border="1">
        <tr>
            <xsl:for-each select="$years/fAnReVal">
                <th>
                    <h5>inside</h5>
                </th>
            </xsl:for-each>
        </tr>
    </table>
</xsl:template>
</xsl:stylesheet>

Solution

  • I could not understand the logic of your XSLT. Is there any reason why you cannot do simply:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
    
    <xsl:key name="data-by-year" match="fAnReVal" use="substring-before(rtnDate, '-')" />
    
    <xsl:template match="/catalog">
        <xsl:variable name="years" select="performanceVO/bmCalYearDet/bmCalDet" />
        <table border="1">
            <!-- header -->
            <tr>
                <th/>
                <xsl:for-each select="$years">
                    <th>
                        <xsl:value-of select="calYear" />
                    </th>
                </xsl:for-each>
            </tr>
            <!-- 1st row -->
            <tr>
                <th>rtnWOSC</th>
                <xsl:for-each select="$years">
                    <td>
                        <xsl:value-of select="key('data-by-year', calYear)/rtnWOSC" />
                    </td>
                </xsl:for-each>
            </tr>
            <!-- 2nd row -->
            <tr>
                <th>rtnWSC</th>
                <xsl:for-each select="$years">
                    <td>
                        <xsl:value-of select="key('data-by-year', calYear)/rtnWSC" />
                    </td>
                </xsl:for-each>
            </tr>
            <!-- 3rd row -->
            <tr>
                <th>val</th>
                <xsl:for-each select="$years">
                    <td>
                        <xsl:value-of select="val" />
                    </td>
                </xsl:for-each>
            </tr>
        </table>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Applied to your input example, the (rendered) result will be:

    enter image description here

    If you remove a fAnReVal node, the corresponding cells will be empty. For example, using the same input without this part:

        <fAnReVal>
            <fNumber>402</fNumber>
            <rtnDate alt="12/31/2016">2016-12-31</rtnDate>
            <rtnWOSC alt="-17.16">-17.16</rtnWOSC>
            <rtnWSC alt="-21.92">-21.92</rtnWSC>
        </fAnReVal>
    

    will result in:

    enter image description here


    Note that this assumes there is at most one fAnReVal node per year.


    Added:

    To do this in the opposite direction - i.e. create the columns from the 10 most recent fAnReValnodes - you could do:

    XSLT 2.0

    <xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
    
    <xsl:key name="bmCalDet-by-year" match="bmCalDet" use="calYear" />
    
    <xsl:template match="/catalog">
        <xsl:variable name="catalog" select="." />
    
        <xsl:variable name="years">
            <xsl:for-each select="performanceVO/fAnReVal">
                <xsl:sort select="rtnDate" order="descending"/>
                <xsl:if test="position() le 10">
                    <xsl:copy-of select="."/>
                </xsl:if>
            </xsl:for-each> 
        </xsl:variable> 
    
        <table border="1">
            <!-- header -->
            <tr>
                <th/>
                <xsl:for-each select="$years/fAnReVal">
                    <th>
                        <xsl:value-of select="substring-before(rtnDate, '-')" />
                    </th>
                </xsl:for-each>
            </tr>
            <!-- 1st row -->
            <tr>
                <th>rtnWOSC</th>
                <xsl:for-each select="$years/fAnReVal">
                    <td>
                        <xsl:value-of select="rtnWOSC" />
                    </td>
                </xsl:for-each>
            </tr>
            <!-- 2nd row -->
            <tr>
                <th>rtnWSC</th>
                <xsl:for-each select="$years/fAnReVal">
                    <td>
                        <xsl:value-of select="rtnWSC" />
                    </td>
                </xsl:for-each>
            </tr>
            <!-- 3rd row -->
            <tr>
                <th>val</th>
                <xsl:for-each select="$years/fAnReVal">
                    <td>
                        <xsl:value-of select="key('bmCalDet-by-year', substring-before(rtnDate, '-'), $catalog)/val" />
                    </td>
                </xsl:for-each>
            </tr>
        </table>
    </xsl:template>
    
    </xsl:stylesheet>