Link to home
Start Free TrialLog in
Avatar of colly92002
colly92002Flag for United Kingdom of Great Britain and Northern Ireland

asked on

!CDATA <TR> tags are being converted to >TR< (XSLT)

Hello.

When my XML contains a node called "new_line", I would like to my XSLT to write out hte HTMl to close the table row and open a new one.

Since the parse does not like this (I know I am being naughty) I came up with the following:

            <xsl:when test = "name(.) = 'new_line'">
                <!--Need to break up the line -->

                  <![CDATA[
                  </TR>
                  <TR>
                  ]]>

            </xsl:when>
Unfortunately this converts the < and > into "&lt;" and "&gt;"

I tried wrapping it in <script> tags which does not convert it, but then the browser ignores the tags!

Any ideas?

Thanks.

(P.S. ideally (I think) I should develop some kind of xlst that will take take any XML convert this into further XML with additional sub-nodes <LINE1> <LINE2> etc, depending upto the the contents of the XML (i.e. where it finds "new_line" nodes).  My XSLT could then call templates for each subnode.  However I don't know how to do this but if you could point me in the direction of a tutorial I would be most grateful!).
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Hi colly92002,

Here is what you could do
  <xsl:text disable-output-escaping="yes">&lt;/tr>&lt;tr></xsl:text>

but this is highly not recommended

You should always try to find a solution that is wellformed
You introduce a very procedural way of programming that can cause
maintenace issues and guarantueed bugs in XSLT

The XSLT way would be to process all the nodes between two <new_line/> elements
inside a <tr></tr>

I will post an example in a minute

Cheers!
colly92002,

with this XML
<?xml version="1.0" encoding="UTF-8"?>
<test>
    <cell>A1</cell>
    <cell>A2</cell>
    <cell>A3</cell>
    <new_line/>
    <cell>B1</cell>
    <cell>B2</cell>
    <cell>B3</cell>
    <new_line/>
    <cell>C1</cell>
    <cell>C2</cell>
    <cell>C3</cell>
    <new_line/>
    <cell>D1</cell>
    <cell>D2</cell>
    <cell>D3</cell>
</test>

this XSLT would create the table rows, without reverting to output escaping tricks

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <html>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="test">
        <table border="1">
            <xsl:for-each select="new_line">
                <xsl:choose>
                    <!-- if only one new_line -->
                    <xsl:when test="not(preceding-sibling::new_line) and not(following-sibling::new_line)">
                        <tr><xsl:apply-templates select="preceding-sibling::*"/></tr>
                        <tr><xsl:apply-templates select="following-sibling::*"/></tr>
                    </xsl:when>
                    <!-- if the first newline -->
                    <xsl:when test="not(preceding-sibling::new_line)">
                        <tr><xsl:apply-templates select="preceding-sibling::*"/></tr>
                    </xsl:when>
                    <!-- if the last new_line -->
                    <xsl:when test="not(following-sibling::new_line) ">
                        <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)"/>
                        <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)"/>
                        <tr><xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) &gt; $prevNodePos]"/></tr>
                        <tr><xsl:apply-templates select="following-sibling::*"/></tr>
                    </xsl:when>
                    <!-- all other new_line -->
                    <xsl:otherwise>
                        <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)"/>
                        <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)"/>
                         <tr><xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) &gt; $prevNodePos]"/></tr>
                    </xsl:otherwise>
                </xsl:choose>
               
            </xsl:for-each>
        </table>
    </xsl:template>
    <xsl:template match="cell">
        <td><xsl:value-of select="."/></td>
    </xsl:template>
</xsl:stylesheet>

This is a quick hack,
you could easily develop something nicer using recursion,
but I just wanted to get the idea accross

cheers

Geert
Avatar of colly92002

ASKER

Thank you so much for your time!

I will try and build the *proper* answer into my application tomorrow, although I may need a little more help appying it to my current xslt/XML as each "cell" has an unique name (the column name from the the SQL that creates the XML - other than that I can see where you are coming from).

Thanks again!

Iain.
don't hesitate to post part of your XML/XSLT if you need help
cheers
I think I am starting to get to grips with the modular XML stuff now but I still struggle to break out of my procedural habits.

I have modified your XSLT to my application and have nearly got it working.

Here is the XML (note using randomised example data):
- <selectrows>
- <selectrow>
  <recordnumber>0</recordnumber>
  <fnam>Paul</fnam>
  <snam>Walker</snam>
  <adm>01/03/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>35</age>
  <dob>14/09/1967</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>1</recordnumber>
  <fnam>Richard</fnam>
  <snam>Dillon</snam>
  <adm>23/04/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>30</age>
  <dob>18/03/1973</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>2</recordnumber>
  <fnam>Jeffrey</fnam>
  <snam>Davies</snam>
  <adm>25/04/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>30</age>
  <dob>23/05/1973</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>3</recordnumber>
  <fnam>Caroline</fnam>
  <snam>Thomas</snam>
  <adm>03/01/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>31</age>
  <dob>02/02/1972</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>4</recordnumber>
  <fnam>Sue</fnam>
  <snam>Bextor</snam>
  <adm>20/05/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>20</age>
  <dob>27/11/1982</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>5</recordnumber>
  <fnam>Helena</fnam>
  <snam>Bartholemew</snam>
  <adm>01/05/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>16</age>
  <dob>08/08/1986</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>6</recordnumber>
  <fnam>Stephen</fnam>
  <snam>Hickenbottom</snam>
  <adm>12/01/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>25</age>
  <dob>07/01/1978</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <settings>
  <querystring>ReportId%3D954%26sqlid%3D779%26transformfile%3D0%26GroupID%3D106%26param1%3D36%26param2%3D0%26param3%3D0%26param4%3D0%26param5%3D0%26param6%3D0</querystring>
  <groupid>106</groupid>
  </settings>
  </selectrows>



Here is my modified XSLT:
  <?xml version="1.0" encoding="UTF-8" ?>
- <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:include href="http://nhc-ola/OnlineApplications/library/xslt/ReportTemplates.xslt" />
- <xsl:template match="/">
- <html>
- <table class="report" border="1">
  <xsl:apply-templates select="selectrows" />
  </table>
  </html>
  </xsl:template>
- <xsl:template match="selectrows">
- <THEAD>
- <TR class="header">
- <xsl:for-each select="selectrow[1]/*[not(name()='recordnumber')]">
- <xsl:choose>
  <xsl:when test="name(.) = 'doc_title'" />
  <xsl:when test="name(.) = 'report_title'" />
  <xsl:when test="name(.) = 'param_1'" />
  <xsl:when test="name(.) = 'param_2'" />
  <xsl:when test="name(.) = 'dt_groupid'" />
  <xsl:when test="name(.) = 'column_colour'" />
- <xsl:when test="name(.) = 'edit_form'">
- <TD align="left">
- <xsl:call-template name="headerise">
  <xsl:with-param name="inStr" select="name(.)" />
  </xsl:call-template>
  </TD>
  </xsl:when>
  <xsl:when test="name(.) = 'form_edit_rowid'" />
  <xsl:when test="name(.) = 'form_title'" />
  <xsl:when test="name(.) = 'form_edit_mode'" />
  <xsl:when test="name(.) = 'form_set_values'" />
  <xsl:when test="name(.) = 'new_line'" />
- <xsl:otherwise>
- <TD align="left">
- <xsl:call-template name="headerise">
  <xsl:with-param name="inStr" select="name(.)" />
  </xsl:call-template>
  </TD>
  </xsl:otherwise>
  </xsl:choose>
  </xsl:for-each>
  </TR>
  </THEAD>
  <xsl:apply-templates select="selectrow" />
- <!--  Put  any footer information here <TD> and </TD>
  -->
- <TR class="footer">
  <TD colspan="2">Total number of rows returned</TD>
  <TD />
- <TD class="int">
  <xsl:value-of select="count(//selectrow)" />
  </TD>
  </TR>
  </xsl:template>
- <!--  Template to pull the data out of the XML and format it as a report
  -->
- <xsl:template match="selectrow">
- <xsl:for-each select="new_line">
- <xsl:choose>
- <!--  if only one new_line
  -->
- <xsl:when test="not(preceding-sibling::new_line) and not(following-sibling::new_line)">
- <tr>
  ONE 1
  <xsl:apply-templates select="preceding-sibling::*" />
  </tr>
- <tr>
  ONE 2
  <xsl:apply-templates select="following-sibling::*" />
  </tr>
  </xsl:when>
- <!--  if the first newline
  -->
- <xsl:when test="not(preceding-sibling::new_line)">
- <tr>
  <xsl:apply-templates select="preceding-sibling::*" />
  </tr>
  </xsl:when>
- <!--  if the last new_line
  -->
- <xsl:when test="not(following-sibling::new_line)">
  <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)" />
  <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)" />
- <tr>
  <xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) > $prevNodePos]" />
  </tr>
- <tr>
  <xsl:apply-templates select="following-sibling::*" />
  </tr>
  </xsl:when>
- <!--  all other new_line
  -->
- <xsl:otherwise>
  <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)" />
  <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)" />
- <tr>
  <xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) > $prevNodePos]" />
  </tr>
  </xsl:otherwise>
  </xsl:choose>
  </xsl:for-each>
- <!--  My old way - how do I get the above to do this ?
  -->
- <xsl:for-each select="*[not(name()='recordnumber')]">
- <TD class="char">
  <xsl:value-of select="." />
  </TD>
  </xsl:for-each>
  </xsl:template>
  </xsl:stylesheet>


The very last for-each just pulls out all the nodes and formats them (as I do it at the moment - my actual stylesheet does <xsl:choose> here and formats the node diffrently depending upon the contents of the node).  How do I get the "new_line" test bits to "call" (if that is the correct word) a template for each node?

Thanks again.

Iain.

Oops sorry the indenting was lost above.  Here it is again a little clearer:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:include href="http://nhc-ola/OnlineApplications/library/xslt/ReportTemplates.xslt" />


    <xsl:template match="/">
        <html>

              <table class="report" border="1">
                  <xsl:apply-templates select="selectrows"/>
              </table>
        </html>
    </xsl:template>
    <xsl:template match="selectrows">
      <THEAD>
          <TR class="header">

            <xsl:for-each select="selectrow[1]/*[not(name()='recordnumber')]">
              <xsl:choose>
                <xsl:when test = "name(.) = 'doc_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'report_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'param_1'">
                </xsl:when>
                <xsl:when test = "name(.) = 'param_2'">
                </xsl:when>
                <xsl:when test = "name(.) = 'dt_groupid'">
                </xsl:when>
                <xsl:when test = "name(.) = 'column_colour'">
                </xsl:when>
                <xsl:when test = "name(.) = 'edit_form'">
                  <TD align="left">

                  <xsl:call-template name="headerise">
                    <xsl:with-param name="inStr" select="name(.)"/>
                  </xsl:call-template>
               
                 </TD>
                </xsl:when>
                <xsl:when test = "name(.) = 'form_edit_rowid'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_edit_mode'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_set_values'">
                </xsl:when>
                <xsl:when test = "name(.) = 'new_line'">
                </xsl:when>
                <xsl:otherwise>
                  <TD align="left">

                  <xsl:call-template name="headerise">
                    <xsl:with-param name="inStr" select="name(.)"/>
                  </xsl:call-template>
               
                 </TD>
                </xsl:otherwise>
              </xsl:choose>
            </xsl:for-each>
        </TR>
      </THEAD>
      <xsl:apply-templates select="selectrow"/>

        <!-- Put  any footer information here <TD> and </TD> -->
        <TR class="footer">
          <TD colspan="2">Total number of rows returned</TD>
          <TD></TD>
          <TD class="int"><xsl:value-of select="count(//selectrow)" /></TD>
        </TR>

</xsl:template>

    <!-- Template to pull the data out of the XML and format it as a report -->
    <xsl:template match="selectrow">
            <xsl:for-each select="new_line">
                <xsl:choose>
                    <!-- if only one new_line -->
                    <xsl:when test="not(preceding-sibling::new_line) and not(following-sibling::new_line)">
                        <tr>ONE 1 <xsl:apply-templates select="preceding-sibling::*"/></tr>
                        <tr>ONE 2 <xsl:apply-templates select="following-sibling::*"/></tr>
                    </xsl:when>
                    <!-- if the first newline -->
                    <xsl:when test="not(preceding-sibling::new_line)">
                        <tr><xsl:apply-templates select="preceding-sibling::*"/></tr>
                    </xsl:when>
                    <!-- if the last new_line -->
                    <xsl:when test="not(following-sibling::new_line) ">
                        <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)"/>
                        <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)"/>
                        <tr><xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) &gt; $prevNodePos]"/></tr>
                        <tr><xsl:apply-templates select="following-sibling::*"/></tr>
                    </xsl:when>
                    <!-- all other new_line -->
                    <xsl:otherwise>
                        <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)"/>
                        <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)"/>
                         <tr><xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) &gt; $prevNodePos]"/></tr>
                    </xsl:otherwise>
                </xsl:choose>
               
            </xsl:for-each>

          <!-- My old way - how do I get the above to do this ?-->
            <xsl:for-each select="*[not(name()='recordnumber')]">
            <TD class="char"><xsl:value-of select="."/></TD>
            </xsl:for-each>
            
    </xsl:template>



</xsl:stylesheet>

To make it clearer what I am trying to do:

Assuming there is only one "new_line", then I tried to do this:
                    <xsl:when test="not(preceding-sibling::new_line) and not(following-sibling::new_line)">
             <xsl:call-template name="show_row" >
                           <xsl:with-param name="row_data" select="preceding-sibling::*" />
                       </xsl:call-template>
             <xsl:call-template name="show_row" >
                 <xsl:with-param name="row_data" select="following-sibling::*" />
                        </xsl:call-template>
                    </xsl:when>

<!--END SNIPPET -->


Unfortunately, I am not sure how to get the "show_row" template to access the current node list.  I tried to pass it as a parameter but then I don't know how to access it, and then I thought maybe the current node list will be accessible, but unfortunately I cant get the following to work:

 <xsl:template name="show_row">
           <xsl:param name="row_data"/>
          
          <!-- My old way - how do I get the above to do this ?
            <xsl:for-each select="*[not(name()='recordnumber')]">
                -->
        <TR>
            <xsl:for-each select="*[not(name()='recordnumber')]">
            <TD class="char"><xsl:value-of select="."/></TD>
            </xsl:for-each>
          </TR>
    </xsl:template>
Hang on,
in your example I always see the same pattern
so maybe we are just making it too complex

The first three items form the first row,
then there is a new_line element
and then you have the next thre items for the second row
and this in every <selectrow> element

is this pattern always there
If it is we are making it too complex,
I would then recommend to hard code the sequence in your XSLT
colly92002,

having said that,
all you need is this

    <xsl:template match="selectrow/*">
        <xsl:if test="not(name()='recordnumber')">
            <TD class="char"><xsl:value-of select="."/></TD>
        </xsl:if>
   </xsl:template>

and get rid of the similar for-each in your stylesheet

cheers

Geert
Thank you!  The mists are clearing.

here is my complete template:
 <xsl:template match="selectrow/*">
      
          <xsl:for-each select=".">
               
             <xsl:choose>
               <xsl:when test = "name(.) = 'doc_href'">
                 <td class="url">
                    <xsl:call-template name="linkurl">
                       <xsl:with-param name="target_url" select="."/> <!-- this is the content of the current node, with name doc_ref -->
                       <xsl:with-param name="title_url" select="following-sibling::doc_title"/> <!-- path depends on relative position of doc_title to doc_ref -->
                  </xsl:call-template>

                </td>  
               </xsl:when>
                <xsl:when test = "name(.) = 'doc_title'">
                </xsl:when>

<!--            Drill through reports -->
                <xsl:when test = "name(.) = 'drill_through'">
                 <td class="url">
                    <xsl:call-template name="linkreport">


                       <xsl:with-param name="target_id" select="."/> <!-- Replace 923 with the number of the report to call -->
                       <xsl:with-param name="param_val_1" select="../param_1"/> <!-- this is the content of the current node (i.e. the contents of the column called "drill_through" in the SQL).  In this example this is sent to the drill through report as parameter 1 -->
                       <xsl:with-param name="param_val_2" select="../param_2"/> <!-- Send the second parameter from the SQL-->
                       <xsl:with-param name="dt_groupid" select="../dt_groupid"/> <!-- Send the groupid from the SQL -->

                       <xsl:with-param name="title_url" select="following-sibling::report_title"/> <!-- The contents of the SQL column "report_title" contains the text that is displayed in the link.   -->
                  </xsl:call-template>

                </td>  
               </xsl:when>
                <!-- NODES TO IGNORE -->
                <xsl:when test = "name(.) = 'report_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'param_1'">
                </xsl:when>
                <xsl:when test = "name(.) = 'param_2'">
                </xsl:when>
                <xsl:when test = "name(.) = 'dt_groupid'">
                </xsl:when>
                <xsl:when test = "name(.) = 'column_colour'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_edit_rowid'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_edit_mode'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_set_values'">
                </xsl:when>      
                <xsl:when test = "name(.) = 'recordnumber'">
                </xsl:when>
                <xsl:when test = "name(.) = 'new_line'">
                  <!--Need to break up the line -->

                  <!--IGNORE-->

                </xsl:when>
                <!--CALL AN ONLINE FORM -->
                <xsl:when test = "name(.) = 'edit_form'">
                  <td class="url">
                    <xsl:call-template name="link_to_form">


                       <xsl:with-param name="target_id" select="."/> <!-- Can hard code this to the number of the forml -->
                       <xsl:with-param name="edit_rowid" select="../form_edit_rowid"/> <!-- if required this should be set to the column from the SQL that contains the unique identifier in the editable table  -->
                      <xsl:with-param name="edit_mode" select="../form_edit_mode"/> <!-- If known can be hard coded to "add" or "edit" -->

                       <xsl:with-param name="param_val_2" select="../param_2"/> <!-- Send the second parameter to the form (not used yet)-->

                      <xsl:with-param name="set_values" select="../form_set_values"/> <!-- List of value pairs (separated with semi-colons) that the form will populate -->

                       <xsl:with-param name="dt_groupid" select="../dt_groupid"/> <!-- Send the groupid from the SQL (if required)-->

                       <xsl:with-param name="form_title" select="following-sibling::form_title"/> <!-- The contents of the SQL column "form_title" contains the text that is displayed in the link.   -->
                  </xsl:call-template>

                </td>  
              </xsl:when>

<!--         Normal DATA row from SQL -->
              <xsl:otherwise>
               <td class="char">
                 <!--COLUMN COLOUR -->
                 <xsl:variable name="col_name"  select="name(preceding-sibling::*[1])"/>
                 <xsl:variable name="col_value"  select="(preceding-sibling::*[1])"/>
                 <xsl:choose>
                   <xsl:when test = "$col_name = 'column_colour'">
                     <xsl:attribute name='style'>
                       color=<xsl:value-of select="$col_value" />
                     </xsl:attribute>
                   </xsl:when>
                 </xsl:choose>
                  <!--DISPLAY THE VALUE -->
                  <xsl:value-of select="."/>
                </td>  
              </xsl:otherwise>
            </xsl:choose>
       
           </xsl:for-each>      


<!--  Basic example
 <xsl:if test="not(name()='recordnumber')">
            <TD class="char"><xsl:value-of select="."/></TD>
        </xsl:if>
-->
   </xsl:template>




I still do a for-each because I want to do something different depending upon the name of the node, which I hope is acceptable practice?
Perhaps it would be better to add an attribute to the node and process it depending upon that?  Although that is a whole new question!

Thanks again.

Iain.
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ah that seems so obvious now you have explained it!  I will will find the time to re-code the stylesheet in your suggested manner.

Thank you again for your time, as ever you have been most helpful.

Iain.
you are welcome, Iain