How to remove all child nodes of an XmlElement, but keep all attributes?
Asked Answered
G

3

19

How to remove all child nodes of an XmlElement, but keep all attributes?

Note, that XmlElement.RemoveAll also removes all attributes. What is a clean, elegant and well-performing way to remove all child nodes? In other words, what is best-practice here?

Gasholder answered 10/4, 2013 at 12:14 Comment(0)
P
32

For a truly efficient solution:

e.IsEmpty = true;

is your fastest and simplest option. It does exactly what you requested: all inner text and nested elements are discarded, while attributes are retained.

Phosphorite answered 27/3, 2014 at 9:24 Comment(1)
This is the solution I have been waiting for! I verified using Reflector that this property indeed does exactly what is requested.Gasholder
O
7

Would this solution not be simpler?

while(e.FirstChild != null)
    e.RemoveChild(e.FirstChild);
Oratorical answered 5/9, 2013 at 13:4 Comment(2)
That's kind of what I did. I copied what XmlNode.RemoveAll does.Gasholder
removeAllChildren in the framework would be simpler.Limb
D
3

Option 1 Use elem.InnerXml = ""; The full working code if you need this:

    var doc = new XmlDocument();
    doc.LoadXml("<x a1='a' a2='b'><child1/><child2/></x>");
    var elem = doc.DocumentElement;

    Console.WriteLine(elem.OuterXml);
    Console.WriteLine("HasAttributes " + elem.HasAttributes);
    Console.WriteLine("HasChildNodes " + elem.HasChildNodes);

    elem.InnerXml = "";
    Console.WriteLine(elem.OuterXml);
    Console.WriteLine("HasAttributes " + elem.HasAttributes);
    Console.WriteLine("HasChildNodes " + elem.HasChildNodes);
    Console.ReadLine();

Detailied information what InnerXml do:

    public override string InnerXml
    {
      get
      {
        return base.InnerXml;
      }
      set
      {
        this.RemoveAllChildren();
        new XmlLoader().LoadInnerXmlElement(this, value);
      }
    }

There could be performance issue on LoadInnerXmlElement but because we have empty string it should not be big, because most time will take this method:

internal XmlNamespaceManager ParsePartialContent(XmlNode parentNode, string innerxmltext, XmlNodeType nt)
    {
      this.doc = parentNode.OwnerDocument;
      XmlParserContext context = this.GetContext(parentNode);
      this.reader = this.CreateInnerXmlReader(innerxmltext, nt, context, this.doc);
      try
      {
        this.preserveWhitespace = true;
        bool isLoading = this.doc.IsLoading;
        this.doc.IsLoading = true;
        if (nt == XmlNodeType.Entity)
        {
          XmlNode newChild;
          while (this.reader.Read() && (newChild = this.LoadNodeDirect()) != null)
            parentNode.AppendChildForLoad(newChild, this.doc);
        }
        else
        {
          XmlNode newChild;
          while (this.reader.Read() && (newChild = this.LoadNode(true)) != null)
            parentNode.AppendChildForLoad(newChild, this.doc);
        }
        this.doc.IsLoading = isLoading;
      }
      finally
      {
        this.reader.Close();
      }
      return context.NamespaceManager;
    }

Option 2 Following code:

    XmlNode todelete = elem.FirstChild;
    while (todelete != null)
    {
        elem.RemoveChild(elem.FirstChild);
        todelete = elem.FirstChild;
    }

About performane. Let's look into XmlElement.RemoveAll() it is:

public override void RemoveAll()
{
  base.RemoveAll();
  this.RemoveAllAttributes();
}

Where base.RemoveAll() is exactly:

[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public virtual void RemoveAll()
{
  XmlNode oldChild = this.FirstChild;
  for (; oldChild != null; {
    XmlNode nextSibling;
    oldChild = nextSibling;
  }
  )
  {
    nextSibling = oldChild.NextSibling;
    this.RemoveChild(oldChild);
  }
}

So it is same as I wrote above

Dank answered 10/4, 2013 at 12:32 Comment(3)
I'd have reservations regarding performance for this one. It also feels hacky. I'd like to deal with the XML document on an AST level, not on a string level.Gasholder
I added detailed explanationDank
Ok thanks! I'll put this into an extension method and the problem is now solved well for all times.Gasholder

© 2022 - 2024 — McMap. All rights reserved.