NewUser101
NewUser101

Reputation: 151

XSL XML Looping

I have an XML file that I am using XSL to convert into an html page. I would like to loop through the XML file which contains many parent nodes, and then loop through the child nodes and display the result in an HTML table.

So far I am able to loop through the Parent node and sucessful return them, but when I nest a for-each loop inside there to return the attibutes of the child node, I end up returning the attributes of all child nodes in the document and not the ones specific to the parent node.

Can anyone shed a little light on this.

XML:

<AdminReports xmlns="30/11/2011 09:25:58">
    <AdminReport ID="1">
        <DataSourceInformation DataSourceID="12" Value="DSI_50"/>
    </AdminReport>
    <AdminReport ID="2">
        <DataSourceInformation DataSourceID="23" Value="DSI_30"/>
    </AdminReport>
    <AdminReport ID="3">
        <DataSourceInformation DataSourceID="34" Value="DSI_20"/>
    </AdminReport>
</AdminReports>

XSL:

  <table border="1" cellspacing="2" width="800" bgcolor="white">
 <xsl:for-each select="/*/*[name()='AdminReport']">
       <tr bgcolor="9acd32">
       <table><th>Admin Report Num:</th></table>
       <table><th><xsl:value-ofselect="@ID"/>   </th></table>
    </tr>
    <tr>    
     <xsl:for-each select="/*/*/*[name()='DataSourceInformation']"> 
      <table><th>Data Report ID:</th></table>
              <table><th><xsl:value-of select="@DataSourceID"/></th></table>
     </xsl:for-each>
     </tr>
    </xsl:for-each>
</table>

Upvotes: 0

Views: 810

Answers (4)

Visual Stuart
Visual Stuart

Reputation: 661

It is easier to think of XSLT as the declarative template matching engine that it is. Look at the xsl:template and xsl:apply-template elements in this sample. Best wishes!

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ns="30/11/2011 09:25:58">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/ns:AdminReports">
    <table border="1" cellspacing="2" width="800" bgcolor="white">
      <xsl:apply-templates select="ns:AdminReport"/>
    </table>
  </xsl:template>

  <xsl:template match ="ns:AdminReport">
      <tr bgcolor="9acd32">
        <th>Admin Report Num:</th>
        <th>
          <xsl:value-of select="@ID"/>
        </th>
      </tr>
      <tr>
        <xsl:apply-templates select="ns:DataSourceInformation" />
      </tr>
  </xsl:template>

  <xsl:template match="ns:DataSourceInformation" >
      <table>
        <th>Data Report ID:</th>
      </table>
      <table>
        <th>
          <xsl:value-of select="@DataSourceID"/>
        </th>
      </table>
  </xsl:template>

</xsl:stylesheet>

Upvotes: 1

Wayne
Wayne

Reputation: 60414

This can be done more cleanly using templates instead of for-each:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                              xmlns:x="30/11/2011 09:25:58">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:template match="/">
        <table border="1" cellspacing="2" width="800" bgcolor="white">
            <xsl:apply-templates/>
        </table>
    </xsl:template>
    <xsl:template match="x:AdminReport">
        <tr bgcolor="9acd32">
            <table><th>Admin Report Num:</th></table>
            <table><th><xsl:value-of select="@ID"/></th></table>
        </tr>
        <tr><xsl:apply-templates/></tr>
    </xsl:template>
    <xsl:template match="x:DataSourceInformation">
        <table><th>Data Report ID:</th></table>
        <table><th><xsl:value-of select="@DataSourceID"/></th></table>
    </xsl:template>
</xsl:stylesheet>

Notes:

  • Each section gets its own template, which more clearly structures the stylesheet
  • It will be easier to handle new elements in the future using this approach
  • I registered your namespace to the prefix x so that I could reference elements like x:DataSourceInformation instead of *[name()='DataSourceInformation']
  • for-each is rarely needed in XSLT; templates are almost always the more natural solution
  • If you insist on for-each, then look at @GSerg's answer

Upvotes: 2

GSerg
GSerg

Reputation: 78155

You're over-compicating this.

select is relative to the current context node:

<table border="1" cellspacing="2" width="800" bgcolor="white">
    <xsl:for-each select="/*/*[name()='AdminReport']">
       <tr bgcolor="9acd32">
       <table><th>Admin Report Num:</th></table>
       <table><th><xsl:value-of select="@ID"/>   </th></table>
       </tr>
       <tr> 
       <xsl:for-each select="*[name()='DataSourceInformation']">    
           <table><th>Data Report ID:</th></table>
           <table><th><xsl:value-of select="@DataSourceID"/></th></table>
       </xsl:for-each>
       </tr>
     </xsl:for-each>
</table>

Upvotes: 1

Siva Charan
Siva Charan

Reputation: 18064

There is no space between value-of & select

<xsl:value-ofselect="@ID"/>

It should be <xsl:value-of select="@ID"/>

Upvotes: 0

Related Questions