|
Subject:
|
recursion in XSL
|
|
Posted By:
|
jekkos
|
Post Date:
|
1/2/2007 12:42:06 PM
|
Hello All,
I have been experiencing some problems while trying to write a recursive template in XSL. The formatting of my xml docs is as follows:
<nestedElement> <tagName>tag</tagName> <someTag> <nestedElement> <tagName>test</tagName> <fieldAttribute> <name>attribute1</name> <var>temp</var> </fieldAttribute> <nestedElement> <tagName>test1</tagName> </nestedElement> </nestedElement> </nestedElement>
The template should loop through all the nestedElements, and the path in which the template is enterng should be stored in a variable. Still I can't get this to work for some weird reason. As far as I can get is this code.
<xsl:template name="nestedElement"> <xsl:param name="level" select="nestedElement" /> <xsl:for-each select="$level"> <xsl:variable name="nestedElementName" select="tagName" /> //this element is the output I want, together with the attributes that will be processed by the template underneath it <xsl:element name="{$nestedElementName}"> <xsl:call-template name="fieldAttribute" /> <xsl:value-of select="testvalue" /> </xsl:element>
//I use this test for checking whether there is another element nested.. if so, recursion should occur.
<xsl:if test="nestedElement/tagName != """> <xsl:variable name="nextlevel" />
//I tried to add this attriubte: select", "concat ($level, '/nestedElement')", but gives an error.. <xsl:call-template name="nestedElement"> <xsl:with-param name="level" /> </xsl:call-template> </xsl:if> </xsl:for-each> </xsl:template>
//this is the template for the fieldAttribute resolving <xsl:template name="fieldAttribute"> <xsl:for-each select="fieldAttribute"> <xsl:variable name="tempname" select="name" /> <xsl:variable name="tempvalue" select="var" /> <xsl:attribute name="{$tempname}"> <xsl:copy-of select="$tempvalue" /> </xsl:attribute> </xsl:for-each> </xsl:template>
the variable nextlevel should store the current root level, e.g. nestedElement/nestedElement, and increment each time the loop is entering a new child node.. each 'nestedElement' created with the variable nestedElementName should be nested in its appropriate level..
output should be like
<tag1> <test attribute1='temp'> <test1></test1> </tagName> </tagName>
as far as this code is, the template (with some changes) works for two levels
I would of have doen this by passing that variable as parameter with the new call.
Stills IE keeps giving errors about the undefined variable level.. or something about only one element being able to exist in the top node?
for a quick look at the whole thing.. it is online at http://onuris.groept.be/ENG/eTech/a06_eTech08/register_kot.php
anyone has any ideas on this one? Took me a lot of time to sort it out
thanks in advance,
Jeroen
|
|
Reply By:
|
mhkay
|
Reply Date:
|
1/2/2007 1:47:19 PM
|
I think this:
<xsl:if test="nestedElement/tagName != """>
should simply be
<xsl:if test="tagName">
to test if the context item has a child called tagName. (But I may have misunderstood your logic).
//I tried to add this attriubte: select", "concat ($level, '/nestedElement')", but gives an error..
I'm not sure where you tried to put it, and you haven't told us what the error message was, so it's hard to help. However, it looks a bit to me as if you're trying to construct XPath expressions dynamically. You can't do that in standard XSLT (well, you can, but there's no way to evaluate them without an extension function such as saxon:evaluate()). This sounds like the wrong design approach. So perhaps it would be best to go back to first principles: what are you actually trying to achieve?
>Stills IE keeps giving errors about the undefined variable level..
Please quote actual error messages. You might not understand them, but there's a good chance that someone else will.
Also, please note that IE is about the worst XSLT development environment you could possibly choose. Its error messages are notoriously poor. It's fine for deploying your stylesheets once they work, but not for developing and debugging.
Michael Kay http://www.saxonica.com/ Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
|
|
Reply By:
|
jekkos
|
Reply Date:
|
1/2/2007 1:58:42 PM
|
<b>Notice</b>: Undefined variable: level in <b>D:\Websites\ENG\eTech\a06_eTech08\kotModule.php</b> on line <b>188</b><br /> ^ This is the error I get when adding the following attribute to my variable (see template code in previous post) <xsl:variable name="nextlevel" select="concat($level, '/nestedElement')" />
So I just meant the select attribute. or maybe I should get the value-of select="$level" first and then do the concat?
And yes, I think I would like to have some kind of dynamic XPath reference to run the following statement <xsl:for-each select="$level">
where level should recursively increase, according to the number of elements nested.. e.g. I have three levels of nested elements, first: $level should first select the parent node (so it should have the value /nestedElement) second: it should select the first child (path is /nestedElement/nestedElement), finally: process the last ($level is /nestedElement/nestedElement/nestedElement).. is this truly impossible?
thanks for your help alright.
|
|
Reply By:
|
mhkay
|
Reply Date:
|
1/2/2007 3:08:08 PM
|
Can't see why you would get this error, it's presumably because I'm not seeing the whole code.
As I say, I think you need a completely different approach to this problem, so it would help to explain what you are trying to do. The sample output you've shown
<tag1> <test attribute1='temp'> <test1></test1> </tagName> </tagName>
isn't well-formed XML, so I've really very little idea what your requirements are.
Michael Kay http://www.saxonica.com/ Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
|
|
Reply By:
|
jekkos
|
Reply Date:
|
1/2/2007 3:35:38 PM
|
My mistake, it should be like this.
<tag1> <test> <test1></test1> </test> </tag1>
The nestedElement template should iterate a nestedElement tree. the input could be
<nestedElement> <tagName>tag1</tagName> <nestedElement> <tagName>test</tagName> <nestedElement> <tagName>test1</tagName> </nestedElement> </nestedElement> </nestedElement>
the template should select each child with the next recusrion, and create an element with the textnode of the tagName element in that node. I hope that this clarifies my idea.
I try to store the path expression in a variable, so somehow the template know where he left the tree when the next recursion occurs..
I hope you get the idea firt..
last try from me :
<xsl:template name="nestedElement"> <xsl:param name="level" select="'nestedElement'" /> <xsl:variable name="nextlevel" select="concat()"/> <xsl:for-each select="$level">
so the level variable should go through the three directories first it should select th first nestedElement in the for-each second it should select the nestedElement/nestedElement and third it should select the nestedElement/nestedElement/nestedElement with the for-each statement so I should have some variable to track what the last path was and then get the path to his nestedElement children if there there are nestedElement children in the that selected node.. that's it I he can't resolve the variable under the for-each because the level variable will be behind a x directories after x recursions.. maybe that's why he can't resolve? the for-each select statement is variable..?
thanks a lot for your help so far
|
|
Reply By:
|
jekkos
|
Reply Date:
|
1/2/2007 3:50:20 PM
|
I think I should get the path to the node where the for-each statement is in every recursion.
or just the value of $level in the for-each statement..
|
|
Reply By:
|
mhkay
|
Reply Date:
|
1/2/2007 4:22:26 PM
|
I think you're making it far too complicated. So far it looks like this:
<xsl:template match="nestedElement"/> <xsl:apply-templates select="tagName"/> </xsl:template>
<xsl:template match="tagName"> <xsl:element name="{.}"> <xsl:apply-templates select="following-sibling::nestedElement"/> </xsl:element> </xsl:element>
Michael Kay http://www.saxonica.com/ Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
|
|
Reply By:
|
jekkos
|
Reply Date:
|
1/2/2007 4:39:32 PM
|
Okay thank you, looks nice and simple..
but what do I have to do if the nestedElement aren't root elements in my xml?
that doesn't seem to work so far..
|
|
Reply By:
|
jekkos
|
Reply Date:
|
1/2/2007 5:16:57 PM
|
Sorry to bother you with maybe these simple questions.. but I'm pretty new to xsl, and I dont have an idea about how to make the most optimal design.
also for the nestedElements, each time there can be several attributeFields as children. These should be processed to with the template I wrote above. the tags are
<fieldAttribute> <name>attributename</name> <var>value</value> </fieldAttribute>
I tried what you said, it works Okay, but then the attributes aren't added. Your solution also works for one level of nestedElements, the children of the first one don't seem to get in there. another example from my xml:
<nestedElement> <nestedElement> <tagName>option</tagName> <testvalue>Zwarte Zustersstraat</testvalue> <fieldAttribute> <name>value</name> <var>Zwarte Zustersstraat</var> </fieldAttribute> </nestedElement> <tagName>select</tagName> <fieldAttribute> <name>value</name> <var>Zwarte Zustersstraat</var> </fieldAttribute> </nestedElement>
thanks for your time!
|