Recursive ColdFusion Custom Tag Example

Posted July 2, 2007 at 8:00 AM by Ben Nadel

Tags: ColdFusion

I've shown several examples of recursive functions in ColdFusion, but it occurred to me that I have never shown any recursive ColdFusion custom tag examples. ColdFusion custom tags can be invoked from within their own code just the same as a function can. For this example, we will create a ColdFusion custom tag that will output the nodes of an XML document of an undetermined nesting depth. Because the nested child depth is not known, we cannot use a set number of FOR loops to output it. That is where the beauty of recursion comes in; we don't have to know how deep something like an XML document is nested because we will just keep drilling down until we hit a bottom.

To start off, let's build an XML document and pass it into our ColdFusion custom tag for display:

  • <!---
  • Create an XML document that has some information
  • for the very beautiful and highly delicious Maria Bello.
  • By using an XML document, we will be creating an
  • eaily navigatable document for our recursive tag.
  • --->
  • <cfxml variable="xmlMovies">
  •  
  • <movies>
  • <movie>
  • <name>
  • Flicka
  • </name>
  • <releasedate>
  • 2006
  • </releasedate>
  • <imdb>
  • http://imdb.com/title/tt0434215/
  • </imdb>
  • </movie>
  • <movie>
  • <name>
  • Thank You For Smoking
  • </name>
  • <releasedate>
  • 2005
  • </releasedate>
  • <imdb>
  • http://imdb.com/title/tt0427944/
  • </imdb>
  • </movie>
  • <movie>
  • <name>
  • A History Of Violence
  • </name>
  • <releasedate>
  • 2005
  • </releasedate>
  • <imdb>
  • http://imdb.com/title/tt0399146/
  • </imdb>
  • </movie>
  • </movies>
  •  
  • </cfxml>
  •  
  •  
  • <!---
  • Output the XML nodes in our movies document. We are
  • going to pass in the primary XML document and let
  • the tag recursively call itself to display the rest
  • of the nested nodes.
  • --->
  • <cfmodule
  • template="./showxml.cfm"
  • xml="#xmlMovies#"
  • />

Here, we are just creating an XML document of movies staring the crazy insane hot Maria Bello. Then, we hand the XML document object off to the ColdFusion custom tag, showxml.cfm, which outputs the XML document:

  • <movies>
  • <movie>
  • <name>
  • Flicka
  • </name>
  • <releasedate>
  • 2006
  • </releasedate>
  • <imdb>
  • http://imdb.com/title/tt0434215/
  • </imdb>
  • </movie>
  • <movie>
  • <name>
  • Thank You For Smoking
  • </name>
  • <releasedate>
  • 2005
  • </releasedate>
  • <imdb>
  • http://imdb.com/title/tt0427944/
  • </imdb>
  • </movie>
  • <movie>
  • <name>
  • A History Of Violence
  • </name>
  • <releasedate>
  • 2005
  • </releasedate>
  • <imdb>
  • http://imdb.com/title/tt0399146/
  • </imdb>
  • </movie>
  • </movies>

All we had to do was pass off the XML document object to the showxml.cfm tag. The tag, then drills down displaying XML nodes as it finds them:

  • <!--- Kill extra output. --->
  • <cfsilent>
  •  
  • <!---
  • This is the XML object that is going to be
  • displayed. This might be an XML document or
  • it might be an XML node.
  • --->
  • <cfparam
  • name="ATTRIBUTES.Xml"
  • type="xml"
  • />
  •  
  • <!---
  • This is the depth of the XML node. We will
  • need this to indent the XML node on output.
  • --->
  • <cfparam
  • name="ATTRIBUTES.Depth"
  • type="numeric"
  • default="0"
  • />
  •  
  •  
  •  
  • <!---
  • Check to see if the passed in XML object was
  • a document. If it is, then grab the root node
  • reference and store that back into the attribute.
  • --->
  • <cfif IsXmlDoc( ATTRIBUTES.Xml )>
  •  
  • <!--- Get the root node reference. --->
  • <cfset ATTRIBUTES.Xml = ATTRIBUTES.Xml.XmlRoot />
  •  
  • </cfif>
  •  
  •  
  • <!---
  • ASSERT: At this point, no matter what type of
  • XML data was passed into the custom tag, the
  • ATTRIBUTES.Xml variable now points to an XML
  • document node.
  • --->
  •  
  •  
  • <!--- Create a string for the current depth. --->
  • <cfset strDepth = RepeatString(
  • "&nbsp;&nbsp;&nbsp;&nbsp;",
  • ATTRIBUTES.Depth
  • ) />
  •  
  • <!--- Create a string for the next depth. --->
  • <cfset strNextDepth = RepeatString(
  • "&nbsp;&nbsp;&nbsp;&nbsp;",
  • (ATTRIBUTES.Depth + 1)
  • ) />
  •  
  • </cfsilent>
  •  
  • <cfoutput>
  •  
  • <!--- Output the open node tag. --->
  • #strDepth#
  • &lt;#ATTRIBUTES.Xml.XmlName#&gt;<br />
  •  
  • <!---
  • Check to see if this node has child nodes. If it
  • does, we are going to recurse through those. If
  • not, we are just going to display the node text.
  • --->
  • <cfif ArrayLen( ATTRIBUTES.Xml.XmlChildren )>
  •  
  • <!---
  • Loop over the XML children and send each
  • one recursively to this tag.
  • --->
  • <cfloop
  • index="intI"
  • from="1"
  • to="#ArrayLen( ATTRIBUTES.Xml.XmlChildren )#"
  • step="1">
  •  
  • <!---
  • Call the current tag, but this time, pass
  • in the given child of the current XML node.
  • --->
  • <cfmodule
  • template="./showxml.cfm"
  • xml="#ATTRIBUTES.Xml.XmlChildren[ intI ]#"
  • depth="#(ATTRIBUTES.Depth + 1)#"
  • />
  •  
  • </cfloop>
  •  
  • <cfelse>
  •  
  • <!---
  • Output the node text. When outputing this
  • value, use the NEXT node depth as the text
  • will be indented beyond the current tag.
  • --->
  • #strNextDepth#
  • #ATTRIBUTES.Xml.XmlText#<br />
  •  
  • </cfif>
  •  
  • <!--- Output the close node tag. --->
  • #strDepth#
  • &lt;/#ATTRIBUTES.Xml.XmlName#&gt;<br />
  •  
  • </cfoutput>
  •  
  • <!---
  • Exit out to the tag. We only need to fire
  • the open tag. We don't care about any duplicate,
  • close tag functionality.
  • --->
  • <cfexit method="EXITTAG" />

Notice that we only passed in the XML document object to this tag, but the tag itself takes two attributes, XML and Depth. Initially, we don't care about the depth, which will default to zero, but as the custom tag begins to invoke itself recursively, it passes in an incremented depth value so that the output nests, not only syntactically, but also visually.

Once inside the tag, we check to see what type of XML object we have. The first time the tag gets invoked, we are passing in a true XML document object; however, for all subsequent recursive calls, we are passing in an XML node of the original document. In order to make sure that the algorithm can handle the values uniformly, we check to see if we have an XML document and if we do, we replace it with the root XML node of the document. This allows the rest of the code to always assume it has an XML node reference no matter what was passed in.

Then, in the meat of the tag, we check to see if the passed-in XML node has children. If it does, we loop over the children and pass each one recursively back into the current ColdFusion custom tag. Once we hit a node that has no children, we simply output the node value and close out of that instance of the custom tag.

Recursion is a very powerful tool to have and to understand. It really lets you tackle problems that cannot feasibly be done with a set number of loops. But, be cautious because a poorly thought out recursive algorithm can quickly eat up all you RAM bringing your system to a standstill or potentially taking the server down. Sometimes, it is good to build a maximum depth into a recursive algorithm, especially one where not option needs to be explored (such as in artificial intelligence games).


You Might Also Be Interested In:



Reader Comments

Jul 2, 2007 at 11:28 PM // reply »
76 Comments

History of violence is a hectick movie :D


Jul 3, 2007 at 7:22 AM // reply »
11,246 Comments

Yeah, but its a gooood movie. Plus it has a very surprise appearance by William Hurt who is just totally awesome as well.... plus I saw Maria nekid :D


Jul 3, 2007 at 6:28 PM // reply »
76 Comments

Yeah it is a good movie :)


Jun 18, 2010 at 3:27 PM // reply »
3 Comments

Hey Ben-

I'm trying to get this going. I'm in CF6, so that may be my problem, but I'm getting an error: "The required parameter ATTRIBUTES.Xml was not provided."

Any clues?


Jun 20, 2010 at 8:57 PM // reply »
11,246 Comments

@Todd,

That's odd - sounds like you're not passing in the XML attribute to the custom tag.


Jun 21, 2010 at 2:36 PM // reply »
3 Comments

Ahh. Well, I got a little further, but it looks like CF6 won't take the type="xml" portion of the code. It says "Attribute TYPE has an invalid value." This project is on hold until we upgrade.

Thanks for you help anyway, Ben!


Jun 21, 2010 at 3:19 PM // reply »
11,246 Comments

@Todd,

Hmm. You could try changing to type="string" for the CFParam?


Jun 21, 2010 at 4:33 PM // reply »
3 Comments

Genius! WooHoo! Thanks so much!


Jun 21, 2010 at 4:34 PM // reply »
11,246 Comments

@Todd,

Sweeeet :)


Sep 21, 2010 at 6:03 AM // reply »
1 Comments

i appreciate this forum alot.the issue is that there is so much dearth of coldfusion developers out there. is it like coldfusion is not as robust as other languages or whats d issue?


Sep 22, 2010 at 10:12 PM // reply »
11,246 Comments

@Tunji,

The good news is that there are more ColdFusion developers than in the past; the trend is definitely climbing. The language is getting more and more powerful. So, hopefully, the public image will start to improve.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 24, 2013 at 5:39 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam Oops! My mistake! I hadn't gotten that far in my testing - I'm still baby stepping my way through the process. ... read »
May 24, 2013 at 5:13 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
Hi Jason, Thanks for checking up on that, but I still stand firm on my position. :) There are actually two listLast()'s in use, and you're right that the one using a space as a delimiter is fine. ... read »
May 24, 2013 at 4:45 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Ben I have been lurking your site for quite some time, and haven't stepped up to comment until today. Thanks for all the great info - keep it up! @Adam I believe you are mistaken... as the commen ... read »
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools