tags:

views:

703

answers:

3

I whant to loop through a XML file thats look like this:

<node>
  <cd name="td1">
    <data value="cd1-0" />
    <cd name="td2">
      <data value="cd1-1" />
    </cd>
    <cd name="td3">
      <data value="cd1-2" />
    </cd>
  </cd>
  <cd name="td4">
    <data value="cd2-0" />
  </cd>
</node>

This is the result that i what is this.

<html>  
  <table border="1">  
    <tr>  
      <td>cd1-0</td>  
      <td></td>  
    </tr>  
    <tr>  
      <td></td>  
      <td>cd1-1</td>  
    </tr>  
    <tr>  
      <td></td>  
      <td>cd1-2</td>  
    </tr>  
    <tr>  
      <td>cd2-0</td>  
      <td></td>  
    </tr>  
  </table>  
</html>

In this examle I have 2 levels on the node cd. But the level can be infinite. So i need some sort of recursive loop function.

A: 

Hi Patrik,

The XML that you sent does not look like a well formed XML. Leaving that to you, you can use foreach element in xslt.

eg:

<table>
<xsl:for-each select="node/cd">
<tr>
<td>
<xsl:value-of select="data/@value"/>
</td>
</tr>
</xsl:for-each>
</table>

Check this link for more: http://www.w3schools.com/Xsl/xsl_for_each.asp

Hope you got the idea.

Update: Thanks to subtenante for clearing up the xml. You can use template to solve this

<xsl:template match="node">
 <html>
  <body>
   <table border="1">
      <xsl:apply-templates select="cd" />
   </table>
  </body>
 </html>
</xsl:template>

<xsl:template match="cd">
 <tr>
  <td>
   <xsl:value-of select="@name" />
  </td>
  <td>
   <xsl:value-of select="data/@value"/>
  </td>
 </tr> 

 <xsl:if test="cd">
  <xsl:apply-templates select="cd" />
 </xsl:if>
</xsl:template>

This will produce the following table:

<table border="1">
<tbody>
<tr>
 <td>td1</td>
 <td>cd1-0</td>
</tr>
<tr>
 <td>td2</td>
 <td>cd1-1</td>
</tr>
<tr>
 <td>td3</td>
 <td>cd1-2</td>
</tr>
<tr>
 <td>td4</td>
 <td>cd2-0</td>
</tr>
</tbody></table>

The provided xslt needs to be modified a bit to achieve exactly what you want. If you are fine with this. Great.

Source:1

Ramjee

rAm
You don't need that `xsl:if` test at the end. `<xsl:apply-templates select="cd"/>` will only do anything if there's one or more child `cd` elements; checking to see if there's one first is redundant.
Robert Rossney
Thanks Robert, agreed.
rAm
A: 

One way to do it:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <!-- adapt output method and doctype to your needs -->
  <xsl:output 
    method="html" 
    doctype-system="http://www.w3.org/TR/html4/strict.dtd" 
    doctype-public="-//W3C//DTD HTML 4.01//EN" 
    indent="yes" 
  /> 

  <!-- the document root becomes html -->
  <xsl:template match="/">
    <html>
      <xsl:apply-templates select="*" />
    </html>
  </xsl:template>

  <!-- node becomes table -->
  <xsl:template match="node">
    <table border="1">
      <xsl:apply-templates select="*" />
    </table>
  </xsl:template>

  <!-- 1st level cd elements (children of node) go into first td -->
  <xsl:template match="node/cd">
    <tr>
      <td><xsl:value-of select="data/@value" /></td>
      <td />
    </tr>
  </xsl:template>

  <!-- 2nd level cd elements (children of cd) go into second td -->
  <xsl:template match="cd/cd">
    <tr>
      <td />
      <td><xsl:value-of select="data/@value" /></td>
    </tr>
  </xsl:template>

</xsl:stylesheet>
Tomalak
+2  A: 

This will work with any level of imbricated cd elements.

You have to modify a bit to produce the <html> (and <head/><body>...</body>) structure, this can be in the match='node' template.

It will skip the empty trailing <td/> which are not needed for rendering.

XSL

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >

    <xsl:template match="node">
       <xsl:element name="table">
        <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

    <xsl:template match="cd">   
        <xsl:element name="tr">
            <xsl:for-each select="ancestor::cd">
                <xsl:element name="td"/>
            </xsl:for-each>
            <xsl:element name="td">
                <xsl:value-of select="./data/@value" />
            </xsl:element>
        </xsl:element>
        <xsl:apply-templates />
    </xsl:template>

</xsl:stylesheet>

Output

<table>
  <tr><td>cd1-0</td></tr>        <!-- Here the second <td/> is skipped -->
  <tr><td/><td>cd1-1</td></tr>
  <tr><td/><td>cd1-2</td></tr>
  <tr><td>cd2-0</td></tr>        <!-- Here the second <td/> is skipped -->
</table>
subtenante