views:

298

answers:

1

Given the following XML:

<cfsavecontent variable="xml">
<root>
    <parent>
     <child>I'm the first</child>
     <child>Second</child>
     <child>3rd</child>
    </parent>
    <parent>
     <child>Only child</child>
    </parent>
    <parent>
     <child>I'm 10</child>
     <child>I'm 11!</child>
    </parent>
</root>
</cfsavecontent>

Is this the best way to loop over each parent and then extract all children from that parent?

<cfset xml = XMLParse(Trim(xml))>

<cfset parents = XMLSearch(xml, "//parent")>

<cfloop array="#parents#" index="parent">

    <cfset parent = XMLParse(parent)><!--- Is this needed? --->

    <cfset children = XMLSearch(parent, "//child")>

    <cfloop array="#children#" index="child">
     <cfoutput>#child.XmlText#</cfoutput>
    </cfloop>

</cfloop>

The reason I ask is because I've never been able to extract all child elements from the current XML element.

The 'Is this needed?' comment highlights the line I added to make the proceeding line work. But is it possible to to remove this line and somehow change 'XMLSearch(parent, "//child")' to only get child elements from the current 'parent'?

Thanks.

+5  A: 
<cfset parent = XMLParse(parent)><!--- Is this needed? --->

No, it's not. It's even a performance penalty because you create a new DOM this way.

You get an array of XML nodes back from XmlSearch() (why else would you use <cfloop array... ?). This means these should be equivalent:

<!-- new CF8 syntax -->   
<cfloop array="#parents#" index="parent">
  <cfdump var="#parent#">
</cfloop>

<!-- old syntax -->   
<cfloop from="1" to="#ArrayLen(parents)#" index="i">
  <cfdump var="#parents[i]#">
</cfloop>

To make ColdFusion honor context when searching a node, you need to do:

XMLSearch(parent, ".//child")
-------------------^

If you start an XPath expression with "//", ColdFusion obviously searches the entire document the node belongs to, not just that node's descendants.

But if you are interested in outputting all <child> elements from the document, why not do this instead:

<cfset children = XMLSearch(xml, "//child")>
Tomalak
Yup, I get that, but when looping over those parent nodes I want to search within each one.I've been unable to come up with a version of this line...<cfset children = XMLSearch(parent, "//child")>... so I thought, turn 'parent' from an XML Element, into an XML document and then the XPath is easier.
Adrian Lynch
Sorry, commented based on your first draft.I want all child elements, but only for the current parent.
Adrian Lynch
Try to search for ".//child" in the loop. Doing "//child" obviously triggers a document-wide search, whereas starting from "." honors context.
Tomalak
However - looping over all parents, just to output all their children in a loop is pointless. You can select them directly, so I hope it was just an example and your app does something more than just running XmlSearch() in a nested loop. :-)
Tomalak
Yup, just an example. The question should have mentioned context as that's what I was wondering about. Will test is out...
Adrian Lynch
Actually I was so sure that it would do what every sane person would expect that I did not even check what XMLSearch(parent, "//child") actually does. But sure enough, it gets it wrong. *sigh*
Tomalak
Yup, XMLSearch(parent, "//child") gets all children in the document, XMLSearch(parent, ".//child") gets children in the current parent.Cheers Tom!
Adrian Lynch