Groups | Blog | Home
all groups > dotnet xml > december 2005 >

dotnet xml : Remove and insert


Markus
12/2/2005 1:26:03 AM
Hi!

I wanted to select a subset of nodes (list =
selectNodes("parent/child[foo='bar']") from a XmlDocument, then remove all
(parentNode.removeAll();) child-nodes and insert the previous selected nodes
back. Of course the XmlNodes in the nodelist ref. where lost once the
removeAll() were executed, this is expected.

So the obvious way to achive the result would be to clone the nodes or
remove the nodes not qualifying the Xpath.
Well I belive I have found an, for me, unexpected shortcut. If I refer the
list.count property prior to removeAll(), the nodes is still referable and
can be inserted back.

The explanation for this is, as understad it, is that XmlNodeList.Count is a
heap based data type, Integer, and therefore addRef+1 to all selected nodes
when .Count is executed, and therefore they are not marked as collectable.

I've tried searching comments on this one but found nothing. Well, maybe
it's just me that belive this particular behaviour of XmlDocument is
remarkable?

Please comment or correct me, regards,
Markus

P.o.c. (Winform/C#)

XmlDocument doc = new XmlDocument()
doc.LoadXml("<parent><child><foo>bar</foo></child><child><foo>bar</foo></child><child><foo>not bar</foo></child></parent>");

XmlNodeList list = doc.SelectNodes("parent/child[foo='bar']");

MessageBox.Show("Nr. of childs before removeAll()= "+
doc.SelectNodes("parent/child").Count.ToString());

// Remove the comments on the line below to keep nodes in nodelist referable
//int c = list.Count;

doc.SelectSingleNode("parent").RemoveAll();

MessageBox.Show("Nr. of childs after removeAll()= "+
doc.SelectNodes("parent/child").Count.ToString());

foreach(XmlNode item in list)
doc.SelectSingleNode("parent").AppendChild(item);

MessageBox.Show("Nr. of childs after appending list= "+
doc.SelectNodes("parent/child").Count.ToString());





Markus
12/2/2005 10:20:02 AM
Ok, in your code nothings lost. But it will be IF you comment the first line
of list.count: Console.WriteLine("list.Count: {0}.", list.Count);

Snippet:
XmlDocument doc = new XmlDocument();

doc.LoadXml("<parent><child><foo>bar</foo></child><child><foo>bar</foo></child><child><foo>not bar</foo></child></parent>");
XmlNodeList list = doc.SelectNodes("parent/child[foo='bar']");
Console.WriteLine("Before remove, not doing list.Count!");
//Console.WriteLine("Before remove: {0}, doing list.Count!", list.Count);
doc.DocumentElement.RemoveAll();

Output without executing first list.count:
Before remove, not doing list.Count!
list.Count: 0.

Output executing first "list.count":
Before remove: 2, doing list.Count!
list.Count: 2.

What i try to say is that if you do not execute the first list.count, your
nodeLIst will be lost.

Best regards and have a nice weekend!
Markus





[quoted text, click to view]
Martin Honnen
12/2/2005 5:41:49 PM


[quoted text, click to view]


[quoted text, click to view]

XmlDocument doc = new XmlDocument();

doc.LoadXml("<parent><child><foo>bar</foo></child><child><foo>bar</foo></child><child><foo>not
bar</foo></child></parent>");
XmlNodeList list = doc.SelectNodes("parent/child[foo='bar']");
Console.WriteLine("list.Count: {0}.", list.Count);
doc.DocumentElement.RemoveAll();
Console.WriteLine("list.Count: {0}.", list.Count);

gives

list.Count: 2.
list.Count: 2.

for me so why is something lost? .NET has its own conventions about
XmlNodeList instances, those returned by SelectNodes are not live
collections so they don't change when the document changes. It is
different in .NET with XmlNodeList instances returned by
GetElementsByTagName, those are live collections that are updated when
the document changes so this example:

XmlDocument doc = new XmlDocument();

doc.LoadXml("<parent><child><foo>bar</foo></child><child><foo>bar</foo></child><child><foo>not
bar</foo></child></parent>");
XmlNodeList list = doc.GetElementsByTagName("child");
Console.WriteLine("list.Count: {0}.", list.Count);
doc.DocumentElement.RemoveAll();
Console.WriteLine("list.Count: {0}.", list.Count);

gives

list.Count: 3.
list.Count: 0.


--

Martin Honnen --- MVP XML
Martin Honnen
12/4/2005 1:47:02 PM


[quoted text, click to view]


[quoted text, click to view]

Right, as far as I currently understand what we observe is that there
seems to be some lazy evaluation happening, meaning the XmlNodeList
instance returned by SelectNodes is only built once its content is
accessed (as our examples do with outputting the Count property).
So what nodes you find in the XmlNodeList does not depend on the XML
document or node contents when SelectNodes call happens and returns the
XmlNodeList instance but when the Count is accessed first.

I am currently asking elsewhere whether that is a bug or an intended
feature. It looks more like a bug in my understanding, if it is a
feature then it needs proper documentation.

Current test case is

XmlDocument doc = new XmlDocument();

doc.LoadXml("<parent><child><foo>bar</foo></child><child><foo>bar</foo></child><child><foo>not
bar</foo></child></parent>");
XmlNodeList list1 = doc.SelectNodes("parent/child[foo='bar']");
XmlNodeList list2 = doc.SelectNodes("parent/child[foo='bar']");
XmlNodeList list3 = doc.SelectNodes("parent/child[foo='bar']");
Console.WriteLine("Before removeAll:");
Console.WriteLine("list1.Count: {0}.", list1.Count);
doc.DocumentElement.RemoveAll();
Console.WriteLine("After removeAll:");
Console.WriteLine("list1.Count: {0}.", list1.Count);
Console.WriteLine("list2.Count: {0}.", list2.Count);
doc.DocumentElement.InnerXml =
"<child><foo>bar</foo></child><child>Kibo</child>";
Console.WriteLine("After insertion:");
Console.WriteLine("list1.Count: {0}.", list1.Count);
Console.WriteLine("list2.Count: {0}.", list2.Count);
Console.WriteLine("list3.Count: {0}.", list3.Count);

That results then in the output

Before removeAll:
list1.Count: 2.
After removeAll:
list1.Count: 2.
list2.Count: 0.
After insertion:
list1.Count: 2.
list2.Count: 0.
list3.Count: 1.

Thus although all SelectNodes call are identical and are made before the
document is being modified those three lists end up with different Count
property values depending on when Count is being first accessed.

--

Martin Honnen --- MVP XML
AddThis Social Bookmark Button