usr
usr

Reputation: 171178

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

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?

Upvotes: 19

Views: 13353

Answers (3)

Parsley
Parsley

Reputation: 356

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.

Upvotes: 34

Chris Walsh
Chris Walsh

Reputation: 3523

Would this solution not be simpler?

while(e.FirstChild != null)
    e.RemoveChild(e.FirstChild);

Upvotes: 7

Piotr Stapp
Piotr Stapp

Reputation: 19830

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

Upvotes: 3

Related Questions