#1
  1. I fail at spelling
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    Sep 2003
    Location
    NDAuNjIxMTExLC03OS4xNTU=
    Posts
    3,232
    Rep Power
    1775

    XSLT for-each question


    Hey everyone,

    It's been a really long time since I've asked a question. I am now working for a company that's doing some really awesome stuff. The problem is that I'm tasked with something that my mind is just having problems grasping.

    Here's my XML:
    Code:
    <?xml version="1.0"?>
    <assets><search><field>device</field><field>organization</field></search><results><asset key='1'><device>123</device><organization>1</organization></asset><asset key='2'><device>3</device><organization>2</organization></asset></results></assets>
    Here's my XSL
    Code:
    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:template match="/">
      <xsl:apply-templates/>
    </xsl:template>
    
    <xsl:template match="assets">
      <table>
        <tr style="background-color:#ccff00">
          <xsl:for-each select="search/field">
            <th><xsl:value-of select="text()" /></th>
          </xsl:for-each>
        </tr>
        <xsl:for-each select="results">
          <tr>
            <xsl:for-each select="asset">
              <td>Found one</td>
            </xsl:for-each>
          </tr>
        </xsl:for-each>
      </table>
    </xsl:template>
    
    </xsl:stylesheet>
    What I am trying to do is make a table that has:
    device | organization
    123 | 1
    3 | 2

    | = td's

    What am I doing wrong here? I am learning XSLT and so far it's hacky. I really need some guidance so I can learn this better.
    Last edited by chadsmith729; January 27th, 2011 at 08:22 PM. Reason: the scaled back XML was ... ummm... wrong.
    I am working now with Symfony2, Twig, Doctrine, Composer, Assetic, and HTML5. Enjoying doing what I do everyday!
  2. #2
  3. Transforming Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    14,112
    Rep Power
    9398
    *cough* The XML is missing an </asset>.


    It seems to me like there will be only one <results> node. That means you don't need to be for-eaching over it. There will, however, be multiple <asset>s, so you need a loop there.
    Code:
    <xsl:for-each select="results/asset">
    	<tr>
    	<!-- you are now inside an asset node -->
    	</tr>
    </xsl:for-each>
    Each column (which means another loop) comes from a <field> in <search>, so loop over those.
    Code:
    <xsl:for-each select="results/asset">
    	<tr>
    	<xsl:for-each select="../../search/field">
    		<td>
    		<!-- you are now inside a field node -->
    		</td>
    	</xsl:for-each>
    	</tr>
    </xsl:for-each>
    Now you need two bits of information: (1) the name of the field and (2) the corresponding node in the current <asset>.
    Getting the field name is easy with text(), but you're in a <field> so the original <asset> is gone. Solution: a variable. Looking ahead, you'll need a variable for the field name too.
    Code:
    <xsl:for-each select="results/asset">
    	<xsl:variable name="asset" select="." />
    
    	<tr>
    	<xsl:for-each select="../../search/field">
    		<xsl:variable name="field" select="text()" />
    
    		<td>
    		<!-- you are now inside a field node -->
    		</td>
    	</xsl:for-each>
    	</tr>
    </xsl:for-each>
    All that's left is getting the right value-of expression. Starting at the <asset>, find the child node named (whatever the field name is). Or
    Code:
    asset/*[name()=field/text()]
        and using the variables from before,
    $asset/*[name()=$field]
    Code:
    <xsl:for-each select="results/asset">
    	<xsl:variable name="asset" select="." />
    
    	<tr>
    	<xsl:for-each select="../../search/field">
    		<xsl:variable name="field" select="text()" />
    
    		<td>
    			<xsl:value-of select="$asset/*[name()=$field]" />
    		</td>
    	</xsl:for-each>
    	</tr>
    </xsl:for-each>
    Q: So why the $field variable? Weren't we already inside a <field>?
    A: Yes, we were. But inside the selector in the value-of expression the context changed again: according to the currently-evaluated node (which is one of the child nodes in the <asset>).

    Comments on this post

    • chadsmith729 agrees : Perfect, I had to adapt slightly for my real live data but perfect! Thank you so much.
  4. #3
  5. I fail at spelling
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    Sep 2003
    Location
    NDAuNjIxMTExLC03OS4xNTU=
    Posts
    3,232
    Rep Power
    1775
    req,

    Thanks let me try that at work when I have a lot more sleep and some coffee. I'll let you know how it goes and it makes total sense. So how long does the variable live for? Can it be used later or should I loop again?

    Oh ... errr also thanks for pointing out my XML flaw. I scaled my sample back a LOT so I can make it easier to demonstrate what I need to do.

    Thanks,
    Chad
    I am working now with Symfony2, Twig, Doctrine, Composer, Assetic, and HTML5. Enjoying doing what I do everyday!
  6. #4
  7. Transforming Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    14,112
    Rep Power
    9398
    Here's the formal definition.

    tl;dr: A <variable> is accessible to younger siblings and descendants of siblings. Define it as high up in the hierarchy as you can; if you need it elsewhere then you have to set it again.
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2009
    Posts
    191
    Rep Power
    49
    never use for-each for walk into a tree
    foreach is use for node set

    better way
    Code:
    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    	<xsl:template match="/">
    		<html>
    			<body>
    				<xsl:apply-templates select="assets"/>
    			</body>
    		</html>
    	</xsl:template>
    
    	<xsl:template match="assets">
    		<table>
    			<tr style="background-color:#ccff00">
    				<xsl:apply-templates select="search"/>
    			</tr>
    			<xsl:apply-templates select="results"/>
    			<!--
    			<xsl:for-each select="results">
    				<tr>
    					<xsl:for-each select="asset">
    						<td>Found one</td>
    					</xsl:for-each>
    				</tr>
    			</xsl:for-each>
    			-->
    		</table>
    	</xsl:template>
    	<xsl:template match="search">
    		<xsl:apply-templates select="field"/>
    	</xsl:template>
    
    
    
    	<xsl:template match="field">
    		<th>
    			<xsl:value-of select="."/>
    		</th>
    	</xsl:template>
    
    
    	<xsl:template match="results">
    
    		<xsl:apply-templates select="asset"/>
    	</xsl:template>
    
    	<xsl:template match="asset">
    		<tr>
    			<td><xsl:value-of select="device"/></td>
    			<td><xsl:value-of select="organization"/></td>
    		</tr>
    	</xsl:template>
    </xsl:stylesheet>
    result
    Code:
    <?xml version='1.0' ?>
    <html>
      <body>
        <table>
          <tr style="background-color:#ccff00">
            <th>device</th>
            <th>organization</th>
          </tr>
          <tr>
            <td>123</td>
            <td>1</td>
          </tr>
          <tr>
            <td>3</td>
            <td>2</td>
          </tr>
          <tr>
            <td>123</td>
            <td>1</td>
          </tr>
          <tr>
            <td>3</td>
            <td>2</td>
          </tr>
        </table>
      </body>
    </html>
    Helmut Hagemann Germany

    fallen to the bottom of the facts?
    I reach my hand and we go together


    wer lesen und google kann ist klar im Vorteil
    who read and google is able is clear in the advantage
  10. #6
  11. Transforming Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    14,112
    Rep Power
    9398
    Originally Posted by xml-profi
    never use for-each for walk into a tree
    foreach is use for node set
    1. I'd love to hear an explanation for this.
    2. I am using it on a node set.
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2009
    Posts
    191
    Rep Power
    49
    yes you use a nodeset

    by the use of For-each
    if more memory is used
    as if one the description
    the template uses

    in this example become 3 For_each
    and with the notation from O http://en.wikipedia.org/wiki/Big_O_notation
    signifies n high three
    Helmut Hagemann Germany

    fallen to the bottom of the facts?
    I reach my hand and we go together


    wer lesen und google kann ist klar im Vorteil
    who read and google is able is clear in the advantage
  14. #8
  15. Transforming Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    14,112
    Rep Power
    9398
    1. More memory? More than it takes to go into a call stack four templates deep? Repeatedly?
    2. Either way it's O(n*m) with n=number of fields and m=number of rows - both yours and mine. Mine uses a couple loops while yours uses "function" calls.

IMN logo majestic logo threadwatch logo seochat tools logo