How can I delete specific nodes from an XElement?
Asked Answered
F

6

17

I have created a XElement with node which has XML as below.

I want to remove all the "Rule" nodes if they contain "conditions" node.

I create a for loop as below, but it does not delete my nodes:

foreach (XElement xx in xRelation.Elements())
{
  if (xx.Element("Conditions") != null)
  {
    xx.Remove();
  }
}

Sample:

<Rules effectNode="2" attribute="ability" iteration="1">
    <Rule cause="Cause1" effect="I">
      <Conditions>
        <Condition node="1" type="Internal" />
      </Conditions>
    </Rule>
    <Rule cause="cause2" effect="I">
      <Conditions>
        <Condition node="1" type="External" />
      </Conditions>
    </Rule>
</Rules>

How can I remove all the "Rule" nodes if they contain "conditions" node?

Flews answered 27/1, 2015 at 11:10 Comment(1)
you can't iterate rule elements with foreach while deleting the items. rather you can collection them in a list and the iterate using for loop and delete them.Gragg
A
20

You can try this approach:

var nodes = xRelation.Elements().Where(x => x.Element("Conditions") != null).ToList();

foreach(var node in nodes)
    node.Remove();

Basic idea: you can't delete elements of collection you're currently iterating.
So first you have to create list of nodes to delete and then delete these nodes.

Afraid answered 27/1, 2015 at 11:23 Comment(1)
The importance of the call to 'ToList()' here to materialise the collection of elements shouldn't be underestimate. Scratched my head for a good few minutes before I landed here and noticed the .ToList() call before the foreach utilisation! (Thank you)Culprit
M
15

You can use LINQ:

xRelation.Elements()
     .Where(el => el.Elements("Conditions") == null)
     .Remove();

Or create a copy of the nodes to delete, and delete them after (in case the first method doesn't work):

List nodesToDelete = xRelation
    .Elements()
    .Where(el => el.Elements("Conditions") == null)
    .ToList();

foreach (XElement el in nodesToDeletes)
{
    // Removes from its parent, but not nodesToDelete
    // itself, so we can use foreach here
    el.Remove();
}
Mousebird answered 27/1, 2015 at 11:25 Comment(2)
I don't think your solution at the top can work. el.Elements("Conditions") == null) will always evaluate to not null.Mcneill
@CiaranGallagher well, the answer is 7 years old, but it was upvoted 12 times, so I assume it's been a working solution to OP's issues at the time :)Mousebird
S
3
  passiveLead.DataXml.Descendants("Conditions").Remove();

This will remove all descendant elements that match the name 'Conditions' for the XML document.

Stoss answered 2/11, 2015 at 20:18 Comment(1)
Can you please add some additional explanation or references for the OP and future readers?Invidious
H
2

I've made a small example for you:

XDocument document = XDocument.Parse(GetXml());
var rulesNode = document.Element("Rules");
if (rulesNode != null)
{
    rulesNode.Elements("Rule").Where(r => r.Element("Conditions") != null).Remove();
}
Hibbert answered 27/1, 2015 at 11:24 Comment(0)
S
1
var el = xRelation.XPathSelectElement("/Rules/Rule/Conditions");
while (el != null)
{
      el.Remove();
      el = xRelation.XPathSelectElement("/Rules/Rule/Conditions");
}
Sparoid answered 22/12, 2018 at 11:24 Comment(0)
I
-1

Just an idea:

Reverse the LINQ "condition" and you will get a List without "Rule" nodes.

Isagogics answered 8/1, 2019 at 6:48 Comment(1)
Hi! Small answers must go in comments section. Please wait till you have enough reputation to comment. See stackoverflow.com/help/how-to-answerEhling

© 2022 - 2024 — McMap. All rights reserved.