This code works:
var element = XElement.Parse("<div><span><em>Content</em></span><span><em>Content2</em></span></div>");
var spans = element.Descendants("span").ToList();
foreach(var span in spans)
{
span.ReplaceWith(span.Nodes());
}
This does not, giving the error ‘Object reference not set to an instance of an object.’:
var element = XElement.Parse("<div><span><em>Content</em></span><span><em>Content2</em></span></div>");
var spans = element.Descendants("span");
foreach(var span in spans)
{
span.ReplaceWith(span.Nodes());
}
The only difference is I have removed the ‘ToList()’ when creating the list of Descendant Nodes. Why is this?
IQueryable implements IEnumerable, and I thought iterating forced deferred execution, so why is ‘ToList()’ making a difference here?
In the second case, you’re changing the collection
spanswhile you’re iterating over it, so bad things happen. Maybe it’s trying to give you what it remembers to be the nextspan, but you’ve already removed it.(In core collections like lists, you get an exception explicitly stating that the collection has been modified. For example, in a
List<.>, there’s a version number that changes every time the list is modified; when you callToNexton a list enumerator, if the version has changed, an exception is thrown.In LINQ-to-XML it appears that the developers didn’t add this kind of behaviour.)
In the first case, the list created by
ToListisn’t modified when you modify the original tree.I’d try something like this:
i.e. replace the spans one by one, rather than trying to get them all in one go and then replace them.