Ask Ben: Iterating Over A ColdFusion XML Document

Posted November 15, 2007 at 7:35 AM by Ben Nadel

Tags: ColdFusion, Ask Ben

I would appreciate your help, in providing a workable nested solution for the following type of XML structure.

<Info Main>
<Image List>
<Image>
<Image>
<Name List>
<Name>
<Name>
<Name>
<Name>
</Info Main>
<Info Main>
</Info Main>

Where the Info Main is the top structure containing collections of images and names. I have no problem in looping the Info Main top structure as per:

<cfloop index="i" from="1" to="#arrayLen(Info_Main_Array)#"> etc, but I can't seem to work out how to connect the sub-lists of images and names. Your help would be much appreciated.

For starters, let's just make sure that we're all on the same page as to what constitutes a valid XML document in ColdFusion (or anywhere). In a valid XML document, there has to be some sort of root XML node that contains all the other nodes. Also, I don't believe the XML node names can have spaces in them. So, the node "Info Main" is not a valid name. I don't think the parser would be able to tell where the name ends and a poorly formed attribute begins. So, let's take your XML document and convert into something that the ColdFusion XML parser can actually work with:

  • <!--- Create the XML document object. --->
  • <cfxml variable="xmlDoc">
  •  
  • <info>
  •  
  • <info-main id="im1">
  •  
  • <image-list id="il1">
  • <image id="i1" />
  • <image id="i2" />
  • </image-list>
  •  
  • <name-list id="nl1">
  • <name id="n1" />
  • <name id="n2" />
  • <name id="n3" />
  • <name id="n4" />
  • </name-list>
  •  
  • </info-main>
  •  
  • <info-main id="im2">
  •  
  • <image-list id="il2">
  • <image id="i3" />
  • <image id="i4" />
  • </image-list>
  •  
  • <name-list id="nl2">
  • <name id="n5" />
  • <name id="n6" />
  • <name id="n7" />
  • <name id="n8" />
  • </name-list>
  •  
  • </info-main>
  •  
  • </info>
  •  
  • </cfxml>

As you can see, I have wrapped your XML nodes into a single root node, info. I have also lower cased all the names (remember XML is very much case sensitive) and I have added hyphens in the node names that had spaces. I have also added ID attributes to each node so that as we traverse the ColdFusion XML document object model, we will easily be able to output the ID to track our progress.

When extracting data from a ColdFusion XML document object, we can get really slick and do some cool XmlSearch() stuff with XPath, but, for this, let's keep it to some simple direct XML node access. To do this, you simply have to understand how the ColdFusion XML document is structured. Each node has a set of attributes (XmlAttributes) and a set of direct child nodes (XmlChildren). The XmlAttributes object is a key-value struct. The XmlChildren is an array.

You can get to each node using these two sets mentioned above, or you can also treat the XML document as a pseudo struct/array document. For example, if we had a parent node containing a child node and we wanted to access the second child of the first parent, we could do this:

xmlDoc.data.XmlChildren[ 1 ].XmlChildren[ 2 ]

... or, we could treat it more like a struct / array beast:

xmlDoc.data.parent[ 1 ].child[ 2 ]

Both would give you the same node reference.

That's not the best example since you can't see the real XML strcuture, so let's get back to your example. Using a combination of the two access methods, we can not iterate over the known structure of our test ColdFusion XML document:

  • <!--- Loop over the info-main nodes. --->
  • <cfloop
  • index="intMainIndex"
  • from="1"
  • to="#ArrayLen( xmlDoc.info.XmlChildren )#"
  • step="1">
  •  
  • <!--- Get a short hand for the current info-main node. --->
  • <cfset xmlInfoMain = xmlDoc.info[ "info-main" ][ intMainIndex ] />
  •  
  • info-main[ id = #xmlInfoMain.XmlAttributes.id# ]<br />
  •  
  •  
  • <!--- Get the image-list children. --->
  • <cfset arrImageList = xmlInfoMain[ "image-list" ] />
  •  
  • <!--- Loop over the image list. --->
  • <cfloop
  • index="intImageListIndex"
  • from="1"
  • to="#ArrayLen( arrImageList )#"
  • step="1">
  •  
  • <!--- Get a short hand for the current image-list node. --->
  • <cfset xmlImaegList = arrImageList[ intImageListIndex ] />
  •  
  • . . image-list[ id = #xmlImaegList.XmlAttributes.id# ]<br />
  •  
  •  
  • <!--- Loop over image nodes. --->
  • <cfloop
  • index="intImageIndex"
  • from="1"
  • to="#ArrayLen( xmlImaegList.image )#"
  • step="1">
  •  
  • . . . . image[ id = #xmlImaegList.image[ intImageIndex ].XmlAttributes.id# ]<br />
  •  
  • </cfloop>
  •  
  • </cfloop>
  •  
  •  
  •  
  • <!--- Get the name-list children. --->
  • <cfset arrNameList = xmlInfoMain[ "name-list" ] />
  •  
  • <!--- Loop over the name list. --->
  • <cfloop
  • index="intNameListIndex"
  • from="1"
  • to="#ArrayLen( arrNameList )#"
  • step="1">
  •  
  • <!--- Get a short hand for the current name-list node. --->
  • <cfset xmlNameList = arrNameList[ intNameListIndex ] />
  •  
  • . . name-list[ id = #xmlNameList.XmlAttributes.id# ]<br />
  •  
  •  
  • <!--- Loop over name nodes. --->
  • <cfloop
  • index="intNameIndex"
  • from="1"
  • to="#ArrayLen( xmlNameList.name )#"
  • step="1">
  •  
  • . . . . name[ id = #xmlNameList.name[ intNameIndex ].XmlAttributes.id# ]<br />
  •  
  • </cfloop>
  •  
  • </cfloop>
  •  
  • <br />
  •  
  • </cfloop>

Running the above code, we get the following output:

info-main[ id = im1 ]
. . image-list[ id = il1 ]
. . . . image[ id = i1 ]
. . . . image[ id = i2 ]
. . name-list[ id = nl1 ]
. . . . name[ id = n1 ]
. . . . name[ id = n2 ]
. . . . name[ id = n3 ]
. . . . name[ id = n4 ]

info-main[ id = im2 ]
. . image-list[ id = il2 ]
. . . . image[ id = i3 ]
. . . . image[ id = i4 ]
. . name-list[ id = nl2 ]
. . . . name[ id = n5 ]
. . . . name[ id = n6 ]
. . . . name[ id = n7 ]
. . . . name[ id = n8 ]

I hope this example helps you out in some way.




Reader Comments

Rob
Oct 8, 2008 at 8:44 PM // reply »
7 Comments

Hi Ben,

Can you help me Iterate over this advanced XML object? http://www.nycwebdesign.net/xmlFEED.cfm

That is the link i'm getting...
I have this so far:
<cfscript>
XMLContent = trim(objGet.FileContent);
XMLContent = XMLParse(XMLContent);
</cfscript>

I basically need to output this XML and set the following vars:
Term1MonthlyRate
Term1QuarterlyRate
Term1Semi-AnnualRate
Term1AnnualRate
Term1AppLink

Term2MonthlyRate
Term2QuarterlyRate
Term2Semi-AnnualRate
Term2AnnualRate
Term2AppLink

Thanks!


Oct 9, 2008 at 8:40 AM // reply »
11,243 Comments

@Rob,

Your link is giving me a ColdFusion error.


Rob
Oct 9, 2008 at 10:36 AM // reply »
7 Comments

Hi Ben...try it again. Thanks. I am curious to see how you go about iterating through that XML feed.

Thanks.


Oct 9, 2008 at 1:20 PM // reply »
11,243 Comments

@Rob,

Right now, there data on that page is a CFDump. I would need a page that points to the XML data itself in order to be able to parse and traverse it.


Jun 9, 2009 at 4:33 PM // reply »
44 Comments

Thanks, Ben! This really helped me debug a report I was working on earlier today. I was asked to run a deep text search on all the files on our site in search of some outdated URLs. Then I was to save that report to Excel. My first thought was to run the search with regular expressions and Dreamweaver. The search went through fine. It was getting to save the report in a user-friendly format where I ran into problems.

The only option for saving the search results was a Dreamweaver-specific XML format. (Not even a tab-delimited format? Come on!) That left with me little choice but to figure out how to turn this verbose XML document into something more usable. Enter CF support of XML and my being about six years behind in this area.

I looked through the guides to see how it was supposed to be done. My first attempt seemed rather logical. Using CFFile, I would load the contents of the Dreamweaver XML file to a variable, and then use CFXml to create an XML document object.

No matter what I tried, I kept getting this annoying "Content not allowed in prolog" error. I found a couple of posts on this topic including one on this site. I made sure there were no spaces before the opening XML tag. I even used regular expressions to try and eliminate any potential Byte-Order-Markers (BOMs) at the beginning. Nothing worked.

I'll tell you what did work. The sample XML here. It got parsed with no problems whatsoever. Seeing how I was able to get the CF code working using the sample in this blog post, I got desperate and decided to copy-and-paste the contents of the XML file into my CFXml block. Well what do you know? That worked too!?!??

For some reason I might never know, I could never get CFXml to create the XML object if I read in the XML code using CFFile. If the XML was hard-coded into the page, however, all was dandy. So I was able to create the report, albeit several hours later. But now I know, and knowing is half the yadda yadda yadda.

On a side note: I learned just a few minutes ago that Eclipse's search results are copy-and-paste-able. Maybe next time I'll go with Eclipse :-)


Sep 22, 2010 at 5:04 AM // reply »
4 Comments

Hi Ben,

Thanks for this helpful and clear explanation. Exactly what I was looking for.

Cheers!


Sep 22, 2010 at 2:07 PM // reply »
11,243 Comments

@Michiel,

Awesome - glad this helped. ColdFusion's XML documents are pretty awesome.


Oct 5, 2011 at 10:33 AM // reply »
2 Comments

How would you parse over this?
<body>
<orderCompleteNotificationRequest>
<ResultSuccess="1"/>
<participantID>134D375F-0577-8326-0B33-1C2717549658</participantID>
<externalParticipantID>rortisi@aiag.org</externalParticipantID>
<offerID>b88a9206-0701-45ba-93c4-13de3db8dc04</offerID>
<transactionNumber>TEST TRANSACTION NUMBER</transactionNumber>
<completionStatus>COMPLETE</completionStatus>
<pointCategoryResults>
<pointCategoryResult Title="Placeholder" PointCategoryID="174B7C2A-2007-0520-2718-5F6186597734" Percentage="98" MinBound="0" MaxBound="50" ScoreValue="49"/>
</pointCategoryResults>
</orderCompleteNotificationRequest>
</body>

I can get/pull out everything until I get to the <pointCategoryResults> node. I need to be able to read the <pointCategoryResult> elements of Title and Percentage.


Dec 9, 2011 at 10:29 AM // reply »
11 Comments

Hi Ben
I think i'm having a similar problem to @Rob

I think the problem is being caused because of namespaces.

I have put my content on http://www.fusebox.co.za/terminal_query.cfm where you will see the raw XML as well as the dump.

The XML is returning 2 transactions which i need to get into a digestable format.

Would you mind taking a look?
I'm sure im not the only one going grey because of this.

Thank you so much for sharing your knowledge!


Dec 9, 2011 at 7:20 PM // reply »
11 Comments

Hi all
Just thought I'd share the solution to my question above:

<cfset xmlp = xmlparse(cfhttp.filecontent)>
<cfset xmlitems = xmlp['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:queryResponse']['returnTranData']>
<cfset availableBalance = xmlp['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:queryResponse']['availableBalance']>
<cfoutput>Available balance: #availableBalance#</cfoutput><Br /><Br />
<cfloop from="1" to="#arraylen(xmlitems['item'])#" index="i">
<cfoutput>
TransactionID: #xmlitems['item'][i].TransactionID.xmltext#<br />
TranType: #xmlitems['item'][i].TranType.xmltext#<br />
</cfoutput>
<br />
</cfloop>


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 22, 2013 at 5:35 PM
Script Tags, jQuery, And Html(), Text() And Contents()
This is still an issue 2 years later. jQuery is supposed to remediate these cross browser issues, no? I have been unable to find any statement from the jQuery team calling this behavior "by de ... read »
May 22, 2013 at 12:44 PM
Ask Ben: Query Loop Inside CFScript Tags
In cf10, if you call a function that has: local.result = {}; local.result.msg = ""; local.svc = new query(); local.svc.setSQL("SELECT * FROM..."); local.obj = local.svc.exe ... read »
May 22, 2013 at 12:29 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben: What version of Java are you using? Also, did you test users.id to see what Java reports as the data type? I wonder if it's not a Java primitive data type, but getting returned as something ... read »
May 22, 2013 at 11:47 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dana, Awesome - so it looks like this bug was fixed in ColdFusion 10. Thanks so much for double-checking that. ... read »
May 22, 2013 at 11:37 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
When I c&p and run on cf10, I get: Selected User IDs: 1,4 User 1 selected: YES - YES User 2 selected: NO - NO User 3 selected: NO - NO User 4 selected: YES - YES User 5 selected: NO - ... read »
May 22, 2013 at 11:27 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Tom, Good thought, but no dice. Both of these still exhibit the same behavior: users.id[ users.currentRow ] users[ "id" ][ users.currentRow ] It's just something whacky happening with ... read »
May 22, 2013 at 11:07 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
Could your problem be that "users.id" is actually an ARRAY, not a single value? Perhaps try it again with "users.id[1]" (I only have CF8 here at work). ... read »
May 22, 2013 at 7:52 AM
Nested Views, Routing, And Deep Linking With AngularJS
Hi, Just a quick thank you. As it happens, for my own purposes, the pending ui-router work being done in native angular is likely the one I'll adopt, but your exploration, code and documentation of ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools