Search code examples
xmlxsltxslt-2.0

Need to create the template by using the count of row entry


I'm having input xml be like:

<row>
   <entry colname="col1" colsep="0" rowsep="0">Get</entry>
   <entry colname="col2" colsep="0" rowsep="0">Some</entry>
   <entry colname="col3" colsep="0" rowsep="0">Manual</entry>
</row>
<row>
   <entry colname="col1" colsep="0" rowsep="0">Skip</entry>
</row>
<row>
   <entry colname="col1" colsep="0" rowsep="0" namest="col1" nameend="col3">Temp</entry>
</row>
<row>
   <entry colname="col1" colsep="0" rowsep="0" namest="col1" nameend="col3">Task</entry>
</row>

XSL I have used like below:

<xsl:template match="row">
    <row>
        <xsl:apply-templates/>
    </row>
</xsl:template>

<xsl:template match="entry">
    <entry>
        <xsl:attribute name="colname">
            <xsl:value-of select="@colname"/>
        </xsl:attribute>
        <xsl:attribute name="colsep">
            <xsl:value-of select="@colsep"/>
        </xsl:attribute>
        <xsl:attribute name="rowsep">
            <xsl:value-of select="@rowsep"/>
        </xsl:attribute>
        <xsl:apply-templates/>
    </entry>
</xsl:template>

<xsl:template match="entry[@colname and @namest and @nameend]">
        <entry>
            <xsl:attribute name="colname">
                <xsl:value-of select="@colname"/>
            </xsl:attribute>
            <xsl:attribute name="colsep">
                <xsl:value-of select="@colsep"/>
            </xsl:attribute>
            <xsl:attribute name="rowsep">
                <xsl:value-of select="@rowsep"/>
            </xsl:attribute>
            <xsl:attribute name="namest">
                <xsl:value-of select="@namest"/>
            </xsl:attribute>
            <xsl:attribute name="nameend">
                <xsl:value-of select="@nameend"/>
            </xsl:attribute>
            <xsl:apply-templates/>
        </entry>
    </xsl:template>

Expected output be like:

<row>
   <entry colname="col1" colsep="0" rowsep="0">Get</entry>
   <entry colname="col2" colsep="0" rowsep="0">Some</entry>
   <entry colname="col3" colsep="0" rowsep="0">Manual</entry>
</row>
<row>
   <entry colname="col1" colsep="0" rowsep="0">Skip</entry>
   <entry colname="col2" colsep="0" rowsep="0"/>
   <entry colname="col3" colsep="0" rowsep="0">
</row>
<row>
   <entry colname="col1" colsep="0" rowsep="0" namest="col1" nameend="col3">Temp</entry>
</row>
<row>
   <entry colname="col1" colsep="0" rowsep="0" namest="col1" nameend="col3">Task</entry>
</row>

There is having lot of entries with namest and nameend attribute. The Above entry template is working well for which having 3 entry inside the row. I want to create the new template for entry by using the count of entries inside the row. Please suggest.


Solution

  • Perhaps you can have a variable that stores the maximum number of entry elements in a row...

     <xsl:variable name="cols" select="max(//row[not(entry/@namest)]/count(entry))" />
    

    Then, have a template that matches row elements that don't have the maximum number of entry, and use xsl:for-each to append new empty elements

    <xsl:template match="row[not(entry[$cols])]">
      <xsl:copy>
        <xsl:apply-templates />
        <xsl:for-each select="count(entry) + 1 to $cols">
          <entry colname="col{.}" colsep="0" rowsep="0"></entry>
        </xsl:for-each>
      </xsl:copy>
    </xsl:template>
    

    Try this XSLT:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="2.0">
    
      <xsl:output method="xml" indent="yes" />
    
      <xsl:variable name="cols" select="max(//row[not(entry/@namest)]/count(entry))" />
    
      <xsl:template match="@*|node()">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="row[not(entry[$cols]) and not(entry/@namest)]">
        <xsl:copy>
          <xsl:apply-templates />
          <xsl:for-each select="count(entry) + 1 to $cols">
            <entry colname="col{.}" colsep="0" rowsep="0"></entry>
          </xsl:for-each>
        </xsl:copy>
      </xsl:template>
    </xsl:stylesheet>