Search code examples
xmlxslt

XML rowspan needs to update Dummy <testentry/> element using XSLT


In the xml file we have rowspan and their value and we need to update where the rowspan found and their cell needs to update one <testentry/> element.

I have a sample xml file :

<?xml version="1.0" encoding="UTF-8"?>
<article article-type="COMPLETEDARTICLE" dtd-version="2.3" xml:lang="EN" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">
<table-wrap id="T1" position="float">
<label>TABLE 1</label>
<caption>
<p>Main Title.</p>
</caption>
<table>
<thead valign="top">
<tr>
<th align="center">HEAD1</th>
<th align="center">HEAD2</th>
<th align="center">HEAD3</th>
</tr>
</thead>
<tbody valign="top">
<tr>
<td rowspan="3" align="center">Prototype Fluidic Oscillator</td>
<td align="center">Throat width</td>
<td align="center">25 mm</td>
</tr>
<tr>
<td align="center">semi-spread angleOutlet</td>
<td align="center">218.6 mm</td>
</tr>
<tr>
<td align="center">Feedback channel length</td>
<td align="center">50&#xb0;</td>
</tr>
<tr>
<td rowspan="6" align="center">Throat Modified Fluidic Oscillator</td>
<td align="center">width</td>
<td align="center">25 mm</td>
</tr>
<tr>
<td align="center">semi-spread angle</td>
<td align="center">Outlet 50&#xb0;</td>
</tr>
<tr>
<td rowspan="2" align="center">feedback channel Maximum length</td> #Edited Here
<td align="center">11.8 mm</td>
</tr>
<tr>
<td align="center">Minimum lengthfeedback channel</td>
<td align="center">9.8 mm</td>
</tr>
<tr>
<td align="center">Length of WORKFLOW</td>
<td align="center">2198 mm</td>
</tr>
<tr>
<td align="center">Mixing chamber HEIGHT</td>
<td align="center">76.9 mm</td>
</tr>
</tbody>
</table>
</table-wrap>
</article>

Tried code:

  <xsl:template match="tr">
    <xsl:element name="tr">
      <xsl:attribute name="style" />
      <xsl:variable name="rowspan">
    <xsl:if test="preceding-sibling::tr/child::td/@rowspan">
      <xsl:value-of select="sum(preceding-sibling::tr/child::td/@rowspan)" />
    </xsl:if>
    <xsl:if test="preceding-sibling::tr/child::th/@rowspan">
      <xsl:value-of select="sum(preceding-sibling::tr/child::th/@rowspan)" />
    </xsl:if>
      </xsl:variable>
      <xsl:variable name="j">
    <xsl:value-of select="$rowspan" />
      </xsl:variable>
      <xsl:variable name="td-pos" select="preceding-sibling::tr[child::td/@rowspan]/position()" />
      <xsl:variable name="th-pos" select="preceding-sibling::tr[child::th/@rowspan]/position()" />
      <xsl:for-each select="1 to $td-pos[last()]">
     <xsl:message select="$td-pos" />
    <xsl:message select="'debug message'">
      <xsl:value-of select="$rowspan" />
      <xsl:value-of select="$j" />
      <xsl:variable name="k" select="$rowspan" />
      <xsl:value-of select="$k" />
    </xsl:message>
    <xsl:if test="$j &gt; 0">
      <xsl:element name="testentry" />
    </xsl:if>
    <xsl:variable name="j">
      <xsl:value-of select="$j - 1"/>
    </xsl:variable>
      </xsl:for-each>
      <xsl:for-each select="1 to $th-pos[last()]">
    <xsl:element name="dummyentry" />
      </xsl:for-each>
      <xsl:if test="child::td/@colspan|child::th/@colspan">
    <xsl:variable name="num">
      <xsl:if test="child::th/@colspan">
        <xsl:value-of select="sum(child::th/@colspan)-1" />
      </xsl:if>
      <xsl:if test="child::td/@colspan">
        <xsl:value-of select="sum(child::td/@colspan)-1" />
      </xsl:if>
    </xsl:variable>
    <xsl:for-each select="1 to $num">
      <xsl:element name="td">
        <xsl:attribute name="style" />
      </xsl:element>
    </xsl:for-each>
      </xsl:if>
      <xsl:apply-templates select="@*|node()" />
    </xsl:element>
  </xsl:template>

Expected Output:

<?xml version="1.0" encoding="UTF-8"?>
<article article-type="COMPLETEDARTICLE" dtd-version="2.3" xml:lang="EN" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">
<table-wrap id="T1" position="float">
<label>TABLE 1</label>
<caption>
<p>Main Title.</p>
</caption>
<table>
<thead valign="top">
<tr>
<th align="center">HEAD1</th>
<th align="center">HEAD2</th>
<th align="center">HEAD3</th>
</tr>
</thead>
<tbody valign="top">
<tr>
<td rowspan="3" align="center">Prototype Fluidic Oscillator</td>
<td align="center">Throat width</td>
<td align="center">25 mm</td>
</tr>
<tr>
<testentry/>
<td align="center">semi-spread angleOutlet</td>
<td align="center">218.6 mm</td>
</tr>
<tr>
<testentry/>
<td align="center">Feedback channel length</td>
<td align="center">50&#xb0;</td>
</tr>
<tr>
<testentry/>
<td rowspan="2" align="center">Throat Modified Fluidic Oscillator</td>
<td align="center">width</td>
<td align="center">25 mm</td>
</tr>
<tr>
<testentry/>
<td align="center">semi-spread angle</td>
<td align="center">Outlet 50&#xb0;</td>
</tr>
<tr>
<testentry/>
<td align="center">feedback channel Maximum length</td>
<td align="center">11.8 mm</td>
</tr>
<tr>
<td align="center">Minimum lengthfeedback channel</td>
<td align="center">9.8 mm</td>
</tr>
<tr>
<td align="center">Length of WORKFLOW</td>
<td align="center">2198 mm</td>
</tr>
<tr>
<td align="center">Mixing chamber HEIGHT</td>
<td align="center">76.9 mm</td>
</tr>
</tbody>
</table>
</table-wrap>
</article>

The output inserting all the rows. Its wrong.

I struggled more than an hour. Could you please someone help me on this one. Thanks.


Solution

  • This simple xslt could give you a good start:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="1.0">
      
      <xsl:output indent="yes"/>
      
      <xsl:strip-space elements="*"/>
      
      <xsl:template match="node()|@*">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
      </xsl:template>
      
      <!-- Since rowspan could appear on both tbody and thead lets start by matching on if they have a tr with td/th with @rowspan -->
      <xsl:template match="table/*[tr/*[@rowspan]]">
        <xsl:copy>
          <!-- Lets just start at the first row -->
          <xsl:apply-templates select="tr[1]"/>
        </xsl:copy>
      </xsl:template>
      
      <!-- 
        Assumption: @rowspan on entry means the amount of rows spanned inclusive the current row 
        If you think otherwise just change factorForAssumption to 0
      -->
      <xsl:variable name="factorForAssumption" select="1"/>
      <xsl:template match="tr">
        <xsl:param name="rowspanned" select="0"/>
        <xsl:variable name="rowspan" select="*[1]/@rowspan"/>
        <xsl:copy>
          <xsl:if test="$rowspanned &gt; 0">
            <testentry/>
          </xsl:if>
          <xsl:apply-templates/>
        </xsl:copy>
        <xsl:apply-templates select=" following-sibling::tr[1]">
          <xsl:with-param name="rowspanned">
            <xsl:choose>
              <xsl:when test="$rowspan &gt; $factorForAssumption">
                <xsl:value-of select="$rowspan - $factorForAssumption"/>
              </xsl:when>
              <xsl:when test="$rowspanned &gt; $factorForAssumption">
                <xsl:value-of select="$rowspanned - 1"/>
              </xsl:when>
            </xsl:choose>
          </xsl:with-param>
        </xsl:apply-templates>
      </xsl:template>
        
      
    </xsl:stylesheet>