简体   繁体   中英

Fetching descendant nodes always return null XML C#

I have an XML file that looks like this

<modules>
<level4>
<module>
  <name>Programming Principles I</name>
  <credit>20</credit>
  <assessments>

    <assessment>
      <assessment_name>Course Work 1</assessment_name>
      <weight>50</weight>
      <passing>55</passing>
      <obtain>55</obtain>
    </assessment>
    <assessment>
      <assessment_name>Course Work 2</assessment_name>
      <weight>50</weight>
      <passing>55</passing>
      <obtain>55</obtain>
    </assessment>

  </assessments>
</module>
</level4>
</modules>

Whilst, getting into assessment childs of parent assessments it returns null..

using this code

XDocument xml = XDocument.Load("module.xml");
var result = xml.Descendants("module")
                         .FirstOrDefault(x => (string)x.Element("name") == "Programming Principles I");
var assessments = result.Element("assessments");
var assessment = assessments.Descendants("assessment");
foreach(var a in assessment)
{
    a.Remove();
}

At this assessment variable I am getting null value exception. The LINQ query works fine but the next two lines I am uncertain. I am actually, removing all the nodes present inside assessments node.

The problem is that you cannot remove an item (the item that part of the construct that is being iterated on) in a foreach loop.

Your code:

foreach(var a in assessment)
{
    a.Remove();
}

is the culprit. You may remove an item in a for loop as follows:

var la = assessment.ToList();
for (int i = la.Count; i > 0; --i)
    la[i - 1].Remove();

Modifying a lazily-evaluated query while iterating over it is basically dangerous.

Two options:

  • Use the Remove extension method . Less code, and it'll work! Replace your whole loop with:

     assessment.Remove(); 
  • Evaluate the query before you start removing anything, storing the result as a list. At this point, it's fine to use foreach because removing the element from its parent won't remove it from the list:

     foreach(var a in assessment.ToList()) { a.Remove(); } 

You still have a problem in your code though - you're using FirstOrDefault() when finding the module element, which means you're anticipating that you might not find it - but you're ignoring that possibility in the next line.

I'd probably rewrite the code to this:

var assessmentsToRemove = xml
    .Descendants("module")
    .Where(mod => (string) mod.Element("name") == "Programming Principles I")
    .Elements("assessments")
    .Elements("assessment");
assessmentsToRemove.Remove();

You could do it all in a single statement if you want, but the above approach makes it easier to diagnose issues later in the debugger.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM