29

I need to remove an XmlNode based on a condition. How to do it?

foreach (XmlNode drawNode in nodeList)
{
       //Based on a condition
       drawNode.RemoveAll();  //need to remove the entire node                      

}

6 Answers6

64

This should do the trick for you:

for (int i = nodeList.Count - 1; i >= 0; i--)
{
    nodeList[i].ParentNode.RemoveChild(nodeList[i]);
}

If you loop using a regular for-loop, and loop over it "backwards" you can remove items as you go.

Update: here is a full example, including loading an xml file, locating nodes, deleting them and saving the file:

XmlDocument doc = new XmlDocument();
doc.Load(fileName);
XmlNodeList nodes = doc.SelectNodes("some-xpath-query");
for (int i = nodes.Count - 1; i >= 0; i--)
{
    nodes[i].ParentNode.RemoveChild(nodes[i]);
}
doc.Save(fileName);
Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • 1
    In this particular case it would work since the code is not removing the nodes from the XmlNodeList collection, but from the XmlDocument DOM, but in many other cases deleting items in a forward for-loop may give unexpected results (such as items not being removed). Reflex movement, I guess ;o) – Fredrik Mörk May 17 '09 at 19:09
  • This is the widely used iterative method to remove item from collections.thanks fredrik for the code wise enlightenment to all:) – Ravisha Jan 31 '10 at 05:15
  • Seems to not work with attribute nodes, right? (because of RemoveChild doing nothing in that case) – IgorK May 11 '11 at 07:05
  • 1
    @IgorK: no, attributes work a bit differently. Check this code for a reworked sample for removing attributes from nodes: http://pastebin.com/gLVkKxa4 – Fredrik Mörk May 11 '11 at 08:06
  • +1 for backward looping when removing nodes, works like a charm! – Mike May 01 '14 at 13:12
  • `doc.SelectNodes()` requires an XMLNamespaceManager. – vapcguy Nov 15 '18 at 02:13
  • 1
    @vapcguy There are two overloads, one that requires `XmlNamespacemanager` and one that doesn't: https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlnode.selectnodes?view=netframework-4.7.2#System_Xml_XmlNode_SelectNodes_System_String_ – Fredrik Mörk Nov 15 '18 at 06:51
  • @FredrikMörk Thanks - it's funny, then, because I wanted to simply use the `.SelectNodes(…)` like you did, but got this error when doing it with a Word document: `Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function` and another person got the same error without using that `XMLNamespaceManager` overload on a separate XML construct: https://stackoverflow.com/questions/11202078/namespace-manager-or-xsltcontext-needed-this-query-has-a-prefix-variable-or-u/46373044?noredirect=1#comment93502387_46373044 – vapcguy Nov 15 '18 at 07:00
  • 1
    I think that depends on your query: if the query contains namespaces, then you need to use the overload taking an `XmlNamespaceManager`, otherwise you can use the simpler version. – Fredrik Mörk Nov 15 '18 at 07:02
4

You can not easily use iterators (foreach-statement) to delete your nodes. As I see it you can do the following:

1) In your foreach-loop, save a list of all elements that should be removed. Then you loop through just those elements and remove them.

2) Use an ordinary for-loop and keep track of which is the next element to visit after you have deleted one item.

Edit: When using a for-loop do it the way Fredrik suggests, looping backwards.

Daniel Wedlund
  • 804
  • 7
  • 18
  • 1
    Upvoting for the first answer; part 2 sounds a bit complicated the way you describe it, it's usually easier to iterate over the list in reverse order so you don't have to worry about the next index. – overslacked May 17 '09 at 18:25
  • Why I added "..keep track of which is the next element to visit..." was because I didn't know in which way the for-loop would be implemented, if the nodes are in correct order etc. – Daniel Wedlund May 17 '09 at 18:33
4

If you're trying to remove a node from the XML DOM, this isn't the correct way. Because an XMLNodeList is just a list of nodes. Rather you want to remove the node from the parent node. Like this:

XmlNode parentNode = // some value
XmlNode drawNode = // some value
parentNode.ParentNode.RemoveChild(drawNode);
Matt Wilko
  • 26,994
  • 10
  • 93
  • 143
Keltex
  • 26,220
  • 11
  • 79
  • 111
  • 1
    AFIK, XmlNode "RemoveNode" doesn't exist in C#. – Chris Bennet Oct 12 '12 at 17:59
  • 1
    Indeed, at least at 4.0. I think he meant: drawNode.ParentNode.RemoveChild(drawNode); It's a bit ugly but it does what I'd expect a "remove" function to do: zaps the drawNode and anything in it. – philw Feb 23 '13 at 15:07
  • If `drawNode` is a child of `parentNode`, you don't need `.ParentNode`. You can just simply do `parentNode.RemoveChild(drawNode);` – vapcguy Nov 15 '18 at 01:35
2

Seems to me you're trying to just remove an entire XML Element...

If this is your XML...

<Xml1>
  <body>
    <Book>
      <Title name="Tom Sawyer" />
      <Author value="Mark Twain" />
    </Book>
    <Book>
      <Title name="A Tale of Two Cities" />
      <Author value="Charles Dickens" />
    </Book>
  </body>
</Xml1>

If you wanted to delete a book, you need to grab the first <Book> node. You can do that with:

XmlDocument doc = new XmlDocument();
doc.Load(fileName);

XmlNodeList nodes = doc.GetElementsByTagName("body");
XmlNode bodyNode = nodes[0];
XmlNode firstBook = bodyNode.ChildNodes[0];

Once you have the "first book" node, you can delete that from the body node using:

bodyNode.RemoveChild(firstBook);

This will automatically affect/update the XML Document variable, so doc will now only have:

<Xml1>
  <body>
    <Book>
      <Title name="A Tale of Two Cities" />
      <Author value="Charles Dickens" />
    </Book>
  </body>
</Xml1>

If you want to grab and delete the whole body, you should be able to do:

XmlNodeList xml1 = doc.GetElementsByTagName("Xml1");
XmlNode xmlNode = xml[0];
xmlNode.RemoveChild(bodyNode);

And the doc variable will be updated to no longer contain the body element, and can then be resaved to the file system:

doc.Save(fileName);

Where fileName is the full path to the XML Document on your computer.

Best of all, we aren't using doc.SelectNodes(), so we don't need to worry about using an XMLNamespaceManager.

vapcguy
  • 7,097
  • 1
  • 56
  • 52
2

Isn't the following a little simpler:

XmlDocument doc = new XmlDocument(); 
doc.Load(fileName); 
XmlNodeList nodes = doc.SelectNodes("some-xpath-query"); 
while (nodes.FirstChild != null) {     
    nodes.RemoveChild(nodes.FirstChild); 
} 
doc.Save(fileName); 
0
    XmlNodeList xnodeContact = xmldocContact.GetElementsByTagName("contact");
          foreach (ListViewItem item in listViewContacts.Items)
            {
                if (item.Checked)
                {
                    if (item.Index >= 0)
                        xnodeContact[0].ParentNode.RemoveChild(xnodeContact[0]);
                        listViewContacts.Items.Remove(item);
                    }
                }
            }
            xmldocContact.Save(appdataPath + "\\WhatAppcontactList.xml");
            Invalidate();
Alexander Vogt
  • 17,879
  • 13
  • 52
  • 68
Milind Morey
  • 2,100
  • 19
  • 15