Link to home
Start Free TrialLog in
Avatar of rxraza
rxraza

asked on

foreach is not doing the second child.

hi guys:

I am having a strange problem. I have got this node which has two children from SomeFunction. In the foreach loop it is not doing the second child, if I remove the following statement from the foreach body       
SOCHXRoot.AppendChild(n);

then it would do the second child. I do not know why it is breaking out after appending child to SOCHXRoot.

thanks in advance.


SOCHXRoot = createNode("SOCHX");

XmlNode node = SomeFunction();

// node has two children

foreach(XmlNode n in node.ChildNodes)
      SOCHXRoot.AppendChild(n);

protected XmlElement createNode(string elementName,string elementValue)
{
XmlElement node = doc.CreateElement(elementName);
node.InnerText      = elementValue;
                  
return node;
}
Avatar of kayhustle
kayhustle

This is because it threw an exception.  You cannot modify a collection while you are enumerating it.  You should use a regular for loop and bind it by the number of ChildNodes.
Avatar of rxraza

ASKER

Am I modifying the collection? Also, I put a try catch block around it and got no clue about any exception.

Avatar of rxraza

ASKER

Doing the following is throwing 'object reference not set to an instance' the second time it tries to append.

int count = node.ChildNodes.Count;

for (int i=0;i<count;i++)
     SOCHXRoot.AppendChild(node.ChildNodes[i]);
      
Avatar of rxraza

ASKER

What I am trying to accomplish here is to append children ignoring the parent because parent is just used for decoration purposes. Any ideas???
Avatar of rxraza

ASKER

I took off the looping contruct and did the following.

SOCHXRoot.AppendChild(node.FirstChild);
SOCHXRoot.AppendChild(node.LastChild);

It worked but the problem is not every node that is returned by SomeFunction contains two children.

It is driving me nuts
Avatar of rxraza

ASKER

Though I managed to create some workaround, I still want to know that why

foreach(XmlNode n in node.ChildNodes)
     SOCHXRoot.AppendChild(n);

would not append the second time. I am curious to know, that is why I am adding more points to it.
Try to debug it by displaying the number of elements in node in foreach.

foreach(XmlNode n in node.ChildNodes)
{
//check Count here  
   SOCHXRoot.AppendChild(n);
}

if you are changing the collection in the next line it will be visible
rxraza,

The following is happening.

Let's say your xml looks like this:

<SOCHXRoot>
    <NODE1>node1</NODE1>
    <NODE2>node2</NODE2>
</SOCHXRoot>

What is happening during the foreach?

foreach(XmlNode n in node.ChildNodes)
    SOCHXRoot.AppendChild(n);

It will detect 2 nodes in node.ChildNodes. The first one is node1 and the second one node2. The first run, XmlNode 'n' will be node1. When you call SOCHXRoot.AppendChild(n), what this will do is append node1 to the SOCHXRoot, however, AppendChild will MOVE the node, not copy. So, after the first run, your xml will look like:

<SOCHXRoot>
    <NODE2>node2</NODE2>
    <NODE1>node1</NODE1>
</SOCHXRoot>

As you can see, node1 comes after node2 since when appending it, it will append it as the last child, thus after node2. So, it will have done the first node.

Now, like armoghan says, you ARE changing your collection while enumerating, since you have adjusted the childNodes collection. Since they are not the same as before the first foreach enumeration. Thus, the enumaration WILL terminate.

Hope this helps,

Razzie
Avatar of rxraza

ASKER

Razzie/armoghan:

You are right, In the scenario that Razzie has, the code will modify the collection, but in my case as I put in the very first question, the node (which is returned by SomeFunction) in question is different than the SOCHXRoot and there fore in the foreach loop when I append the child (child does not belong to SOCHXRoot, it belongs to some other node), hence it is not modification of collection, so why on earth I am not able to fully traverse the collection???

XmlElement SOCHXRoot = createNode("SOCHX");

XmlNode node = SomeFunction();

// node has two children AND THE NODE IS NOT THE SAME AS SOCHXROOT

foreach(XmlNode n in node.ChildNodes)
     SOCHXRoot.AppendChild(n);


protected XmlElement createNode(string elementName,string elementValue)
{
XmlElement node = doc.CreateElement(elementName);
node.InnerText     = elementValue;
               
return node;
}

rxraza,

What does SomeFunction() do? How does it create XmlNode node? From another xml? Because if it is another xml document, you can't even append it to SOCHXRoot, because you can't directly insert a node from one xml document to another.
Avatar of rxraza

ASKER

Razzie:

SomeFunction returns a node that is created from the same XML Document using createNode. By the way I figured out that it works fine if I do a clone and then append it.

foreach(XmlNode n in node.ChildNodes){
   XmlNode cloned = n.Clone();                 // clone the node here.
   SOCHXRoot.AppendChild(n);
}

but still I want to find the reason behind it.
 
ASKER CERTIFIED SOLUTION
Avatar of Razzie_
Razzie_

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of rxraza

ASKER

hmmm... that makes sense.. 'REFERENCE TO THE PARENT IS CHANGING'...

anyways, so I was able to make that work by cloning. Is cloning the right choice in this scenario or are there other better ways of getting this job done?
Well I don't know if that was sarcastic, but I take it it was :P

Let's say there are 2 nodes in your collection. When you are enumerating, that collection may *not* change in any way.
When you enumerate the first node in the collection, it has a reference to its parent node, so the xml document knows where the node actually is. When you append that node to SOCHXRoot, you change that reference to its parent node. Because before it was, let's say, SomeNode, and now its parent is SOCHXRoot. There, a change :)

Is it the best way? It depends on what you're trying to do, I guess. If you want to create a new xml document with SOCHXRoot as it's new root, you could use the importnode function, but that works only if you have a second xml document. If SOCHXRoot was now in doc2, you could use:

doc2.ImportNode(n, false) in your foreach loop. If you set the second paramter to true, it will also add the childeren of node n, in case you might want that.
Avatar of rxraza

ASKER

com'n Razzie. I appreciate your help in this matter. 'REFERENECE TO THE PARENT IS CHANGING' is what I extracted from the context and that helped me a lot in developing the understanding of the scenario, Please, do not take that as a sarcasm

Regarding the best way, as you said importnode function will work if I have a second xml document but actually I do not have a second xml document. So, I can rule out this option. Now, to make this logic work I am only left with option of cloning the node first and then appending it. I believe this is the only way out in this scenario, please correct me if I am wrong.

hehe it was no offense, just the way the capital sentence and all sounded :)

Well yes if you're not creating a seperate xml document for SOCHXRoot then I'd use the Clone() method. It's clean and it does the trick. I won't even know another way of doing it, so :)
Avatar of rxraza

ASKER

cool. Well thanks Razzie, I had good time resolving this issue wit you.
same here, glad I could help :)
Also, just came to my mind, but have you tried:

for(int i = 0; i<node.ChildNodes.Count;i++)
    SOCHXRoot.AppendChild(node.ChildNodes[i]);

Since you're not enumerating it it might work, haven't tried it though, but might be something to look at just for fun and educational purposes.