Thursday, July 18, 2013

System.Xml ImportNode and the .NET Heap(s)

If you work with XML in .NET a lot, you have probably bumped into a scenario a few times in your career where you need to move or copy some XML fragment out of one document and into another. The typical coding sequence for that might look something like this:

XmlDocument source = new XmlDocument();
source.LoadXml("");

XmlDocument dest = new XmlDocument();
dest.LoadXml("");

//move from source to dest
XmlNode n = source.SelectSingleNode("/source/myxml");
XmlNode nImported = dest.ImportNode(n, true);
XmlNode nAppended = dest.DocumentElement.AppendChild(nImported);

I have often wondered (but, until today, been too lazy to investigate) why ImportNode() and AppendChild() return XmlNode objects, and what one is supposed to do with them? Are they the same? Clones of the same? Etc.

Well, IronPython happens to have this nice feature where it shows you the memory address of the .NET object you reference in an expression via the ipy console.

Excerpt from an IronPython Console session

(setup a document to import into)
>>> dMS = XmlDocument()
>>> dMS.LoadXml("")


(get some nodes from a "source" document)
>>> nl = d.SelectNodes("/root/mdebug|/root/mdebugr")

(let's experiment with just one)
>>> temp = nl[0]
>>> temp
0x000000000000063D
[System.Xml.XmlElement]>

(import the node from "source" to "dest")
>>> tmpImported = dMS.ImportNode(temp, True)
>>> tmpImported
0x000000000000063F
[System.Xml.XmlElement]>

(now, append the imported node into the "dest" document)
>>> tmpAppended = dMS.DocumentElement.AppendChild(tmpImported)
>>> tmpAppended
0x000000000000063F
[System.Xml.XmlElement]>

 So, we learned a couple of things from this experiment.

  • Based on the memory addresses of temp and tmpImported being different, we can deduce that .NET internally creates a different node (and consequently sub-nodes if the 2nd parameter to ImportNode is True) for the destination XML document.
    • I have often assumed this, simply because of the 2nd parameter asking about "deep" vs "shallow" cloning.
  • Since tmpImported and tmpAppended point to the same object, it appears that the object returned from AppendChild() can optionally be assigned to a reference. This may be useful for more succinct code in certain situations. Otherwise, I plan to typically ignore this returned value by not even storing it.
  • I have often been tempted to skip storing the result of ImportNode, and simply pass to AppendChild my existing XmlNode reference from the source document. Doing so probably would result in an exception, since .NET seems to not allow sharing of XmlNode objects between XmlDocument objects.
Ok, so that doesn't have a lot of street value, but it's a fun experiment to learn about how .NET works internally. :) Enjoy!

No comments:

Post a Comment