Using XmlSearch() In CFLoop Array (Thanks Scott Bennett!)

Posted December 27, 2007 at 8:32 AM by Ben Nadel

Tags: ColdFusion

Yesterday, in the comments on my post about using XML with ColdFusion 8's new array iteration using CFLoop, Scott Bennett posted an example of using an XmlSearch() as the value being passed to the Array attribute in the CFLoop tag. I don't know why exactly, but this just struck me as quite cool (cool enough to get its own ColdFusion 8 post). Here is an example of what he was talking about:

  • <!--- Create xml data. --->
  • <cfxml variable="xmlGirls">
  •  
  • <girls>
  •  
  • <brunette
  • name="Maura Tierney"
  • isactress="yes"
  • />
  •  
  • <blonde
  • name="Christina Cox"
  • isactress="yes"
  • />
  •  
  • <brunette
  • name="Sarah Vivenzio"
  • isactress="no"
  • />
  •  
  • <blonde
  • name="Jodie Foster"
  • isactress="yes"
  • />
  •  
  • <brunette
  • name="Ashley Thomas"
  • isactress="no"
  • />
  •  
  • </girls>
  •  
  • </cfxml>
  •  
  •  
  • <p>
  • <strong>Brunette Girls</strong>
  • </p>
  •  
  • <p>
  • <!---
  • Loop over all the returned xml nodes that are
  • found at the following XPath value.
  • --->
  • <cfloop
  • index="xmlGirl"
  • array="#XmlSearch( xmlGirls, '//brunette' )#">
  •  
  • #xmlGirl.XmlAttributes.name#
  •  
  • <!--- Check to see if this girl is an actress. --->
  • <cfif (
  • StructKeyExists( xmlGirl.XmlAttributes, "isactress" ) AND
  • xmlGirl.XmlAttributes.isactress
  • )>
  •  
  • [Actress]
  •  
  • </cfif>
  •  
  • <br />
  •  
  • </cfloop>
  • </p>

As you can see, for the Array attribute of the CFLoop tag, we are directly passing in the node set returned by the XPath search, XmlSearch( xmlGirls, "//brunette" ). Running the code, we output only the array consisting of the brunette child nodes:

Brunette Girls
Maura Tierney [Actress]
Sarah Vivenzio
Ashley Thomas

This just feels like a much more elegant solution than trying to use the xmlGirls.girls.brunette pseudo array. Also, I think part of why I like it is that it feels much more parallel to the equivalent XSL Transformation:

  • <xsl:for-each select="//brunette">
  •  
  • .....
  •  
  • </xsl:for-each>

I like the symmetry. As a trade-off, you can't refer to the array itself without an intermediary variable, but for these kind of one-offs, it is perfect.

Thanks Scott Bennett!


You Might Also Be Interested In:



Reader Comments

Feb 7, 2008 at 5:15 PM // reply »
10 Comments

Hi Ben,

I have been working on trying looping over data out of an XML file and choosing a particular node through XmlSearch.

XML File Structure:
<propertyList date="2008-01-25-10:02:00" >
<residential modTime="2008-01-25-10:02:01" status="current">
<uniqueID>HI5160</uniqueID>
<images>
<img id="m" file="HI5160_1.jpg"/>
<img id="a" file="HI5160_2.jpg"/>
<img id="b" file="HI5160_3.jpg"/>
<img id="c" file="HI5160_4.jpg"/>
<img id="d" file="HI5160_5.jpg"/>
<img id="e" file="HI5160_6.jpg"/>
<img id="f" file="HI5160_7.jpg"/>
<img id="g" file="HI5160_8.jpg"/>
<img id="h" file="HI5160_9.jpg"/>
<img id="i" file="HI5160_10.jpg"/>
<img id="j" file="HI5160_11.jpg"/>
<img id="k" file="HI5160_12.jpg"/>
</images>
</residential>
</PropertyList

The only way i can get the desired results is this way:

<CFSET propID = getlist.listing_id>

<!--- Loop over the info-main nodes. --->
<cfloop
index="intMainIndex"
from="1"
to="#ArrayLen( Listings.PropertyList.residential )#"
step="1">

<!--- Get a short hand for the current info-main node. --->
<cfset xmlInfoMain = Listings.PropertyList[ "residential" ][ intMainIndex ] />
<cfif xmlInfoMain.uniqueID.XmlText EQ propID>
uniqueID = [<cfoutput>#xmlInfoMain.uniqueID.XmlText#</cfoutput>]<br />

<!--- Get the image-list children. --->
<cfset arrImageList = xmlInfoMain[ "images" ] />

<!--- 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 xmlImageList = arrImageList[ intImageListIndex ] />

<!--- Loop over image nodes. --->
<cfloop
index="intImageIndex"
from="1"
to="#ArrayLen( xmlImageList.img )#"
step="1">

. . . . img[ file = [<cfoutput>#xmlImageList.img[ intImageIndex ].XmlAttributes.file#</cfoutput>] [<cfoutput>#xmlImageList.img[ intImageIndex ].XmlAttributes.id#</cfoutput>]<br />
</cfloop>

</cfloop>
</cfif>
</cfloop>

I'm sure there is a quicker way to do this using XmlSearch.

I have successfully grabbed the record out of the XML i need via this code:

<cfset arrResNodes = XmlSearch(Listings,"//residential[ uniqueID[ text() = '#propID#' ]]/") />

The only problem is i cannot loop over the results of XmlSearch?? Could you point me in the right direction? I can dump the output, just not loop over the img nodes.

Thanks,

Leigh


Feb 8, 2008 at 7:17 AM // reply »
11,246 Comments

@Leigh,

I think you are really close here. To get directly at the array of IMG nodes, you can use this XPath:

//residential[ uniqueID[ text() = 'HI5160' ] ]//img/

where I put in the hard-coded ID for testing. Once you have that array, you just loop over it the same way you are already looping over the image nodes array.


Feb 27, 2008 at 2:40 AM // reply »
10 Comments

Hi Ben,

After your excellent advice which worked like a treat with images, i need your advice on the following:

I have 1 main xml file another xml file with updates only.

I need to append or copy over the main xml with the update data.

I was using the XmlSearch theory: //residential[ uniqueID[ text() = 'HI5160' ] ]

and then trying to append the main file:

<cfset ArrayAppend(mydoc.PropertyList.residential.XmlChildren, XmlElemNew(myupdate,myupdate.PropertyList.residential))>

I would then write the new xml file.

Any further help with this would be appreciated.

Can i use XmlSearch within 1 file and append another file at the same time?

Thanks, Leigh


Feb 27, 2008 at 7:15 AM // reply »
11,246 Comments

@Leigh,

You can't just copy a node from one tree to another. You have to import it first. Check out this UDF that I mad to do that:

http://www.bennadel.com/index.cfm?dax=blog:701.view


Mar 7, 2008 at 2:32 PM // reply »
3 Comments

CF8 allows you to do the

<cfloop index="x" array="#myarray#">
#x# -
</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 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 »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools