Sometimes when you're programming, it is useful to have a function or procedure actually call itself as part of its operation. This is called recursion. Recursion is often useful when dealing with hierarchical data structures. Manipulating data that has a parent-child relationship is an ideal environment to make recursive calls.
XML data, by its very design, is hierarchical. A parent node has one or more children, and any of the children may have children themselves. There's usually no way to know if a particular element will have children, nor will you know how many children it may have. So writing a recursive function to handle the processing of XML data is an effective technique. In this article, I'll show how you can do this with ColdFusion MX.
The Mindmap XML
I use a piece of software called Mindmapper to brainstorm ideas and help me visually architect software. It allows for the arrangement of hierarchical sets of elements into trees and makes it easy to modify these trees. It also allows you to save the data out as XML. I realized that it might be useful to be able to save a Mindmap as XML, parse through it, and use the data in a ColdFusion application. I'll use this scenario as the basis for an example of writing a recursive function.
The XML generated by Mindmapper is structured like Listing A. (It actually has some other elements related to formatting, which I have omitted.)
For this example, I'll build a slightly more complex Mindmap (Figure A) to manipulate. I'll save the Mindmap as an XML file, which I can then use in the recursive function example.
The first step in the process is reading in the XML file and turning it into a ColdFusion XML Object:
<cfset parsedXML = xmlParse( rawXML )>
Basically, xmlParse() turns the XML into a nested series of arrays and structures. I can now manipulate the data using ColdFusion's array and structure functions.
Next, I need to think about what information I want to extract from the XML. In this case, I want each Mindmap element's name, along with its position in the XML hierarchy. By knowing its position, I can output the data later and format it to match the original hierarchy by indenting it, etc.
Now, I can start writing the recursive function itself. I call it recurseMindmapXML(), and it will take two arguments. The first is the path of the element inside the XML Object I want. The first time the function is called, this defaults to parsedxml.MindMapper.item, which is the path to the root element. As the function recursively calls itself, this path will change to reflect the position of the current element. The second argument is depth, meaning the current depth into the XML tree. This is zero when the function is first called, but it also will be updated as the recursion takes place.
The function itself is mainly a loop. You loop through the first element in the XML and capture its name, its depth into the XML tree, and its path in the XML Object. Then, the function looks to see whether the current element has any children. If it does, it recursively calls recurseMindmapXML() and passes updated arguments for the path and depth.
As you iterate through the XML elements, the depth always reflects how far down the XML tree you are. When depth gets back to zero and there are no more child elements to loop over, you know you are finished. The recursion ends and the final data is returned. In a recursive function, it is always critical that the recursion bottom out somewhere. Otherwise, the function will keep calling itself forever. Listing B shows the code for the recurseMindmapXML() function.
The function builds an array of structures. Each array element corresponds to an element from the XML tree, and each array's structure holds the element name, depth, and path. When executed against the sample Mindmap XML, the resulting array of structures looks like Figure B.
After the function runs, the XML data is in a format that can be easily manipulated or output by ColdFusion. For example, the code in Listing C would output each element's name, indented appropriately.
A simple recursion
This simple function is finished, and it does its job well. A nice benefit of using a function like this is that it is easy to modify. If you decide that other information is needed about each element (perhaps its parent), you can add it very quickly. The function can also be generalized to recurse through any type of XML, not just the Mindmap XML. This example should give you an idea of how useful recursive functions can be and how easy they are to create in ColdFusion.