Skip to main content
Ben Nadel at LaunchDarkly Lunch & Learn 2018 (New York, NY) with: Chelsie-Jean Fernandez
Ben Nadel at LaunchDarkly Lunch & Learn 2018 (New York, NY) with: Chelsie-Jean Fernandez ( @haulani7 )

ColdFusion XML Nodes Are Passed By Value, Not By Reference

By on
Tags:

I have been doing some exploration with ColdFusion's XML capabilities and I came up against something very interesting. It seems that the XmlRoot node is passed by reference but any other nodes below that are passed by value. Here is a demo of this in action:

<!--- Create a blank ColdFusion XML document for actresses. --->
<cfset xmlActresses = XmlNew() />

<!---
	Create a new XML node that we will use as the
	XML root. All other nodes in the document will
	live under this node.
--->
<cfset xmlRoot = XmlElemNew( xmlActresses, "", "actresses" ) />

<!---
	Set the new root element as the root node of
	our actresses DOM.
--->
<cfset xmlActresses.xmlRoot = xmlRoot />


<!--- Create a new actress node element. --->
<cfset xmlActress = XmlElemNew( xmlActresses, "", "actress" ) />

<!--- Set some of the actress properties. --->
<cfset xmlActress.XmlAttributes.Name = "Maria Bello" />
<cfset xmlActress.XmlAttributes.HowSexy = "Too Sexy" />


<!---
	Append the actress node to the child nodes of the
	root (Actresses). Notice here that we are using the
	xmlRoot that was generated above and the xmlActress
	node that we just created for Maria Bello.
--->
<cfset ArrayAppend(
	xmlRoot.XmlChildren,
	xmlActress
	) />


<!---
	Now that we have added the new Actress node to the
	XML DOM, let's try to update some of its values without
	going back into the XML document path.
--->
<cfset xmlActress.XmlAttributes.BirthDay = "18 April, 1967" />
<cfset xmlActress.XmlAttributes.Hair = "Blonde" />


<!---
	Now, let's add some attributes, but this time,
	instead of referencing our Actress xml node, let's
	go back through the XML DOM.
--->
<cfset xmlActresses.Actresses.Actress.XmlAttributes.Eyes = "Brown" />
<cfset xmlActresses.Actresses.Actress.XmlAttributes.Smile = "Sly" />



<!--- Now, let's dump out our actress node. --->
<cfdump
	var="#xmlActress#"
	label="xmlActress Node"
	/>

<!---
	Let's dump out our actresses XML DOM. If the nodes are
	passed by reference when using ArrayAppend(), then the
	CFDump above should be reflected in the CFDump below.
--->
<cfdump
	var="#xmlActresses#"
	labe="xmlActressed DOM"
	/>

Notice that when I append the XML node, xmlActress, to the xmlActresses document, I am using the existing node reference, xmlRoot. Also notice that the node being appended is the existing node reference to xmlActress. Since both of these nodes are valid references, it should work fine.

But now, look at what I do after that - I update the xmlActress node properties, adding the two attributes Birthday and Hair. Then, I update the "same" xml node, but this time, I go though the XML DOM, xmlActresses. If the node, xmlActress, was added by reference, then all of these CFSet tags should affect the same element.

Here is a CFDump of the xmlActress node by itself:

ColdFusion XML Actress Post ArrayAppend()

And, here is a CFDump of the resultant xmlActresses XML DOM:

ColdFusion XML Actresses Post ArrayAppend()

Notice that BOTH CFDumps contain the original attributes, Name and HowSexy. These were added prior to any child node appending. Also notice that only the xmlActress has BirthDay and Hair attributes which were set directly into the Xml node reference after it was appended to the xmlActresses DOM.

So clearly, when it comes to XmlAttributes, the XML Nodes are appended by value. But what about the XmlChildren? If you look at the above code, our xmlRoot reference was able to update the resultant document. This means that xmlRoot was somehow set by reference, not by value. Perhaps, ArrayAppend() will work via references for the child nodes as well:

<!---
	Now, let's try to create a child node of the actress node.
	This node will hold a list of all the related movies.
--->
<cfset xmlMovies = XmlElemNew( xmlActresses, "", "movies" ) />

<!---
	Add this movie to the existing xmlActress node reference
	that we created above.
--->
<cfset ArrayAppend(
	xmlActress.XmlChildren,
	xmlMovies
	) />

<!--- Now, let's dump out our actress node. --->
<cfdump
	var="#xmlActress#"
	label="xmlActress Node"
	/>

<!---
	Let's dump out our actresses XML DOM. If the nodes are
	passed by reference when using ArrayAppend(), then the
	CFDump above should be reflected in the CFDump below.
--->
<cfdump
	var="#xmlActresses#"
	labe="xmlActressed DOM"
	/>

Here, we are adding an xml child node, xmlMovies, to the xmlActress node. Here is the CFDump of the xmlActress node:

ColdFusion XML Actress Post ArrayAppend() 2

And, here is the CFDump of the resultant xmlActresses XML DOM:

ColdFusion XML Actresses Post ArrayAppend() 2

Notice that again, the actions to update the child reference did not affect the final XML document. I wonder why xmlRoot was able to act as a reference and xmlActress was only able to act as a copy. I see that xmlRoot was set using "=" operator and the other via ArrayAppend()... but, any value that cannot be passed by reference is copied no matter what operator is used (sort of).

Want to use code from this post? Check out the license.

Reader Comments

67 Comments

Ben,
What you've discovered here is that XML nodes are inserted into an XML document *by value*, not that " ColdFusion XML Nodes Are Passed By Value, Not By Reference".

In all real-world situations I've used them, they are certainly passed by reference.

Sample code:

<cfxml variable="x">
<aaa>
<bbb foo="bar">Hello World</bbb>
</aaa>
</cfxml>
<cfdump var="#x#" label="Original doc">

<cfset a = xmlSearch(x, "/aaa/bbb")>
<cfdump var="#a#" label="Original array">

<!--- update the array --->
<cfset a[1].XmlAttributes["foo"] = "No Foo">
<cfset a[1].Xmltext = "Updated">
<cfdump var="#a#" label="Updated array">

<cfdump var="#x#" label="Updated doc?">

<!--- create a reference to a node --->
<cfset n = x.aaa.bbb>
<cfdump var="#n#" label="Original node">

<!--- update the node reference --->
<cfset n.xmlText = "Updated xmlText by node ref">
<cfset n.XmlAttributes["foo"] = "Updated XmlAttributes value by node ref">
<cfdump var="#n#" label="Updated node">
<cfdump var="#x#" label="Updated doc?">

--
Adam

15,640 Comments

@Adam,

There is something that doesn't quite sit right with me about it. First off, XmlElemNew() requires you to pass in an XML reference when creating a new element. I assume that this is used to set up ownership of the Node before it actually gets put into any node tree. So, at that point, the node should be owned by the document and ready to be inserted into the DOM... what would be the point of adding by value?

And then, to add the node to the DOM, I am just calling an ArrayAppend(). There is nothing inherent to ArrayAppend() that would cause me to think that it would add to the child nodes array by value, not by reference.

I can only assume that behind the scenes, ArrayAppend() is actually checking to see what type of object it is acting on and then is performing a different task when it comes to XML than when it comes to , say, a standard array to which a structure is being appended.

Something about it just seems a little misleading. But then again, maybe it just hasn't had a chance to seep into my mind yet :)

67 Comments

Hi Ben.
I didn't say it was expected, predictable, or that it makes any sense (it's none of those things, from where I'm sitting)! I was more pointing out the inaccuracy of your article heading.

I would expect it to pass-by-reference when first inserting the node (which is what you seem to have expected as well).

--
Adam

15,640 Comments

@Adam,

Sorry, I didn't mean to come off as sounding attacking what you said in any way. I was just venting my slight frustrations with the way it was working.

Yes, you are totally correct in your statement - xml nodes do get passed by reference (which is why getting intermediary pointers to them work as well as XmlSearch). My title could have been much more clear.

67 Comments

I didn't think you were having a go. I was just clarifying what I meant.

:-)

I have another couple of tests up my sleeve... but work has unfortunately got in my way, so I'll have to have a look @ them tomorrow.

--
Adam

5 Comments

Something else I find interesting is what is returned for the parent of the free-standing node and the node from the document.

<cfif structKeyExists(xmlActress, 'XmlParent')>
<cfdump
var="#xmlActress.XmlParent#"
label="xmlActress Parent"
/>
</cfif>

<cfif structKeyExists(xmlActresses.actresses[1].actress, 'XmlParent') >
<cfdump
var="#xmlActresses.actresses[1].actress.XmlParent#"
label="xmlActress DOM parent"
/>
</cfif>

1 Comments

Hi,

I stumbled upon this problem, and found a way around this, the problem seem to be specific to ArrayAppend/ArrayInsertAt

xmlChild = XmlElemNew(Xml,tag);
pos=ArrayLen(xmlbase.XmlChildren);
xmlbase.XmlChildren[pos+1]=xmlChild;

xmlChild.XmlText = "string";

However this is a bit slower than ArrayAppend (using Bluedragon at least)

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel