RE: [xsl] Looping through a tree and retaining parent values

Subject: RE: [xsl] Looping through a tree and retaining parent values
From: Emmanuel Bégué <eb@xxxxxxxxxx>
Date: Wed, 1 Apr 2009 16:25:22 +0200
Hello,

This should work:

<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
	<body>
		<xsl:apply-templates/>
		</body>
	</xsl:template>
<xsl:template match="menuItems">
	<xsl:apply-templates/>
	</xsl:template>

<xsl:template match="menuItem">
	<p id="{@label}">
		<xsl:apply-templates select="ancestor-or-self::menuItem" mode="uic"/>
		</p>
	<xsl:apply-templates/>
	</xsl:template>
<xsl:template match="menuItem" mode="uic">
	<uicontrol>
		<xsl:value-of select="@label"/>
		</uicontrol>
	</xsl:template>

What this does is, for every menuItem in the source,
creates a p element, then populates it with all ancestors
of the current menuItem (including itself) renamed as
uicontrol and having the @label as content.

Regards,
EB

> -----Original Message-----
> From: Mark Peters [mailto:flickrmeister@xxxxxxxxx]
> Sent: Wednesday, April 01, 2009 3:58 PM
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Subject: [xsl] Looping through a tree and retaining parent values
>
>
> Hi Everyone,
>
> I'm puzzling over a transformation that I thought would be simple but
> which becomes progressively more complex as I try to wrap my mind
> around it.
>
> Here is a series of menuItem nodes, which are nested in menuItems
> nodes. Each menuItem node has a label attribute.
>
> <menuConfig>
> 	<menu id="A">
> 		<menuItems>
> 			<menuItem label="1">
> 				<menuItems>
> 					<menuItem label="a"/>
> 						<menuItems>
> 							<menuItem
> label="1a">
> 								<menuItems>
>
> <menuItem label="1a.a"/>
>
> <menuItem label="1a.b"/>
> 								</menuItems>
> 							</menuItem>
> 						</menuItems>
> 					<menuItem label="b">
> 					</menuItem>
> 				</menuItems>
> 			</menuItem>
> 			<menuItem label="2">
> 				<menuItems>
> 					<menuItem label="c"/>
> 					<menuItem label="d">
> 						<menuItems>
> 							<menuItem
> label="2d">
> 								<menuItems>
>
> <menuItem label="2d.d"/>
>
> <menuItem label="2d.e"/>
>
> <menuItem label="2d.f"/>
> 								</menuItems>
> 							</menuItem>
>
> 						</menuItems>
> 					</menuItem>
> 					<menuItem label="e">
> 						<menuItems>
> 							<menuItem
> label="2e">
> 								<menuItems>
>
> <menuItem label="2e.g"/>
> 								</menuItems>
> 							</menuItem>
> 						</menuItems>
> 					</menuItem>
> 				</menuItems>
> 			</menuItem>
> 		</menuItems>
> 	</menu>
> </menuConfig>
>
> What I'm trying to do is create a set of p nodes which nest a series
> of uicontrol nodes, one uicontrol node for each level of menuItem in
> the original XML document. In other words, for each menuItem in the
> original XML, there should be a uicontrol element. The first uicontrol
> node in the series contains the @label value for the parent menuItem
> node. The next uiconcontrol node in the series contains the @label
> value for the first child menuItem node, and so on.
>
> The p node should have an id attribute whose value is the @label value
> of the last child node in each tree.
>
> Here is an example:
>
> <body>
> 	<p id="1">
> 		<uicontrol>1</uicontrol>
> 	</p>
> 	<p id="a">
> 		<uicontrol>1</uicontrol> &gt;
> 		<uicontrol>a</uicontrol></p>
> 	<p id="1a">
> 		<uicontrol>1</uicontrol> &gt;
> 		<uicontrol>a</uicontrol> &gt;
> 		<uicontrol>1a</uicontrol>
> 	</p>
> 	<p id="1.1a">
> 		<uicontrol>1</uicontrol> &gt;
> 		<uicontrol>a</uicontrol> &gt;
> 		<uicontrol>1a</uicontrol> &gt;
> 		<uicontrol>1a.a</uicontrol>
> 	</p>
> 	<p id="1.1b">
> 		<uicontrol>1</uicontrol> &gt;
> 		<uicontrol>a</uicontrol> &gt;
> 		<uicontrol>1a</uicontrol> &gt;
> 		<uicontrol>1b.b</uicontrol>
> 	</p>
> 	<p id="b">
> 		<uicontrol>1</uicontrol> &gt;
> 		<uicontrol>b</uicontrol>
> 	</p>
> 	<p id="2">
> 		<uicontrol>2</uicontrol>
> 	<p id="c">
> 		<uicontrol>2</uicontrol> &gt;
> 		<uicontrol>c</uicontrol>
> 	</p>
> 	<p id="d">
>                 <uicontrol>2</uicontrol> &gt;
> 		<uicontrol>d</uicontrol>
> 	</p>
> 	<p id="2d">
> 		<uicontrol>2</uicontrol> &gt;
> 		<uicontrol>d</uicontrol> &gt;
> 		<uicontrol>2d</uicontrol>
> 	</p>
> 	<p id="2d.d">
> 		<uicontrol>2</uicontrol> &gt;
> 		<uicontrol>d</uicontrol> &gt;
> 		<uicontrol>2d</uicontrol> &gt;
> 		<uicontrol>2d.d</uicontrol>
> 	</p>
> 	<p id="2d.e">
> 		<uicontrol>2</uicontrol> &gt;
> 		<uicontrol>d</uicontrol> &gt;
> 		<uicontrol>2d</uicontrol> &gt;
> 		<uicontrol>2d.e</uicontrol>
> 	</p>
> 	<p id="e">
> 		<uicontrol>2</uicontrol> &gt;
> 		<uicontrol>e</uicontrol>
> 	</p>
> 	<p id="2e">
> 		<uicontrol>2</uicontrol> &gt;
> 		<uicontrol>e</uicontrol> &gt;
> 		<uicontrol>2e</uicontrol></p>
> 	<p id="2e.g">
>                 <uicontrol>2</uicontrol> &gt;
> 		<uicontrol>e</uicontrol> &gt;
> 		<uicontrol>2e</uicontrol> &gt;
> 		<uicontrol>2e.g</uicontrol>
> 	</p>
> </body>
>
> So far, I've come up with a stylesheet that produces a single p node
> for every parent menuItem. It's a start. I've thought about trying to
> use variables to retain the menuItem/@label values at each level in
> the tree; but, reading about variables, I'm not sure this is possible.
> Likewise, I've thought about defining each loop in a separate template
> and then calling each template from the "master" template. But then,
> I'm not sure how I'd retrieve the final menuItem/@label value for the
> p/@id value.
>
> And I must not understand the last() function correctly. The p/@id
> values inserted by the stylesheet are "1a" and "2e.g," and not the
> @label value for the last menuItem in each tree.
>
> <xsl:template match="/">
> 		<xsl:for-each select="//menu">
> 			<body>
> 				<xsl:for-each select="menuItems">
> 					<xsl:for-each select="menuItem">
> 						<p>
>
> <xsl:attribute name="id">
>
> <xsl:value-of select="descendant::menuItem[last()]/@label"/>
>                                                         </xsl:attribute>
> 							<uicontrol>
>
> <xsl:value-of select="@label"/>
> 							</uicontrol> &gt;
>
> <xsl:for-each select="descendant::menuItem[2]">
> 								<uicontrol>
>
> <xsl:value-of select="@label"/>
>
> </uicontrol> &gt;
>
> <xsl:for-each select="descendant::menuItem[3]">
>
> <uicontrol>
>
> 	<xsl:value-of select="@label"/>
>
> </uicontrol> &gt;
>
> 		<xsl:for-each select="descendant::menuItem[4]">
>
> 	<uicontrol>
>
> 		<xsl:value-of select="@label"/>
>
> 	</uicontrol> &gt;
>
> 			<xsl:for-each select="descendant::menuItem[5]">
>
> 		<uicontrol>
>
> 			<xsl:value-of select="@label"/>
>
> 		</uicontrol> &gt;
>
> 			</xsl:for-each>
>
> </xsl:for-each>
>
> </xsl:for-each>
> 							</xsl:for-each>
> 						</p>
> 					</xsl:for-each>
> 				</xsl:for-each>
> 			</body>
> 		</xsl:for-each>
> 	</xsl:template>
>
> I'd appreciate any help you could offer.
>
> Thanks,
> Mark
> --
>
> Mark Peters
> Senior Technical Writer
> Saba Software

Current Thread