Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the New York ColdFusion User Group (Mar. 2008) with:

Accessing XML Nodes Having Names That Contain Dashes In ColdFusion

By Ben Nadel on
Tags: ColdFusion

ColdFusion has always made working with XML documents very easy. By implicitly wrapping the underlying XML DOM nodes in pseudo node collections, we've always been able to easily access XML nodes using struct (dot) and array (bracket) notation. For example, accessing the name of the third girl in an XML document could look as straightforward as something like this:

girls.girl[ 3 ].name.xmlText

As you can see, ColdFusion allows us to think about XML node collections as both named and ordered collections of like-nodes.

Most of the time, this is exactly what we need and it works perfectly. But, as Simon Hume brought up on Twitter today, when your XML node names contain a dash, you have to be a bit more careful in the way you define your node paths. Going back to the previous example, if we had the node, "girl-name", rather than, "name," we might be tempted to try following node path:

girls.girl[ 3 ].girl-name.xmlText

When ColdFusion sees this, however, it will throw the following error:

Element GIRL is undefined in a Java object of type class coldfusion.xml.XmlNodeMap.

This is happening because ColdFusion doesn't see the dash as part of the node name; rather, ColdFusion sees this dash as a mathematical operator in the following equation:

((girls.girl[ 3 ].girl) - (name.xmlText))

To get ColdFusion to see the dash as part of the node name, we have to "escape" it, for lack of a better term. To do so, we either have to use array notation and define the node name as a quoted string; or, we have to use xmlSearch() where we can deal directly with the underlying document object model. The following code demonstrates both of these approaches:

  • <!---
  • Create an XML document that has some node with names that
  • contain dashes.
  • --->
  • <cfxml variable="girls">
  •  
  • <girls>
  • <girl>
  • <given-name>Tricia</given-name>
  • <private-name>Pookie-bear</private-name>
  • </girl>
  • <girl>
  • <given-name>Joanna</given-name>
  • <private-name>Sugar-butt</private-name>
  • </girl>
  • </girls>
  •  
  • </cfxml>
  •  
  •  
  • <!---
  • Use ColdFusion's XML node wrappers to access the pseudo-node
  • collection based on names. Notice here that we are wrapping the
  • node name in quotes and using array-notation in the node path.
  • --->
  • <cfset tricia = girls.girls.girl[ 1 ][ "private-name" ].xmlText />
  •  
  • <!---
  • In this version, we'll use xmlSearch(). Since xmlSearch() is
  • using the native XML DOM structure, we don't need to "escape"
  • the dashed name in any way.
  • --->
  • <cfset joanna = xmlSearch(
  • girls,
  • "/girls/girl[ 2 ]/private-name/"
  • ) />
  •  
  • <!---
  • Output the girl's private names as extracted from the XML
  • document object model.
  •  
  • NOTE: Because xmlSearch() returns an array, "joanna" is the XML
  • node array at this point, not the actual text value.
  • --->
  • <cfoutput>
  •  
  • Tricia: #tricia#
  • <br />
  • <br />
  • Joanna: #joanna[ 1 ].xmlText#
  •  
  • </cfoutput>

As you can see in the above code, our ColdFusion XML document has the dash-containing node, "private-name." In the first approach, we use array notation and quote the node name. In the second approach, we use xmlSearch() in which no "escaping" is necessary. Running the above code, we get the following page output:

Tricia: Pookie-bear

Joanna: Sugar-butt

As you can see, both approaches work fine when dealing with XML nodes that contain dashed-names. The technique you end up using is going to depend on the situation. Using ColdFusion's pseudo node wrappers is typically easier when we know that the structure of the XML document will be fairly static. But, if the XML document can be more dynamic, xmlSearch() gives us a lot of flexibility and power in how we traverse the underlying node tree.




Reader Comments

You could also use native XML traversal to get around this:

xmlRoot.xmlChildren[ 1 ].xmlChildren[ 2 ].xmlText

... but this is pretty much unreadable. As such, I left it out of the convesation; but, I figured I'd throw it here into the comments as an after-thought.

Reply to this Comment

I ran into this issue with an XML attribute, here is how I worked around it...

<cfset example = XmlRoot.XmlAttributes["page-size"]>

...where "page-size" is an XML attribute of the root node. I figure this may be somewhat easier to follow as there is no extraneous array notation, or XML searching involved.

Reply to this Comment

@Justin,

Ah, great points - the array notation works both for element nodes as well as for attribute nodes. Excellent tip.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
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.