David
David

Reputation: 19707

Remove all empty elements using xlinq

I'm doing some transforms using xlinq and some of those transforms can result in leaving empty elements in the document.

Once I am done all of those transforms, how can I query an xdocument for all empty elements?

In other words; if I remove all <a> tags which happen to be the only element inside an <li> tag, how do I remove the empty <li>?

Before:

XDocument.Parse(@"<body>
   <ul><li><a href="#">Joy</a></li></ul>
   <p>Hi.</p>
</body>").Descendants("a").Remove();

After:

<body>
   <ul><li/></ul>
   <p>Hi.</p>
</body>

I would prefer:

<body>
   <p>Hi.</p>
</body>

Upvotes: 1

Views: 4101

Answers (5)

sree
sree

Reputation: 2367

Checking for both IsEmpty and HasAttributes is more helpful.

        var emptyElements = document.Descendants()
                             .Where(element => element.IsEmpty 
                                             && !element.HasAttributes);
        while (emptyElements.Any())
        {
            emptyElements.Remove();
        }

IsEmpty will return true for elements with just attributes. Eg:

      <Participant xsi:nil="true"/>

Upvotes: 0

John Gietzen
John Gietzen

Reputation: 49564

The accepted answer here is not quite right. Specifically, it will only remove elements that are in the form <foo /> and will leave elements like <foo></foo>.

As such, here is a complete solution:

public static void RemoveEmptyDescendants(this XNode node)
{
    var empty = from e in node.Descendants()
                where !e.Nodes().Any() && !e.Attributes().Any()
                select e;

    while (empty.Any())
    {
        empty.Remove();
    }
}

Upvotes: 0

nitin kumar
nitin kumar

Reputation: 21

Give XmlNodeList to this function an try this code Hope this will work to remove all empty element and attribute from XMl file

    public static XmlNode RemoveNullElement(XmlNodeList xmlNodeList)
    {
        if (xmlNodeList.Count > 0)
        {
            foreach (XmlNode xmlnode in xmlNodeList)
            {
                RemoveNullChildAndAttibute(xmlnode);
                return xmlnode;
            }
        }

        return null;
    }


    public static void RemoveNullChildAndAttibute(XmlNode xmlNode)
    {
        if (xmlNode.HasChildNodes)
        {
            for (int xmlNodeCount = xmlNode.ChildNodes.Count - 1; xmlNodeCount >= 0; xmlNodeCount--)
            {
                RemoveNullChildAndAttibute(xmlNode.ChildNodes[xmlNodeCount]);
            }
        }
        else if (xmlNode.Attributes.Count == 0)
        {
            if (xmlNode.ParentNode != null)
            {
                xmlNode.ParentNode.RemoveChild(xmlNode);
            }
        }
    }

Upvotes: 1

Mindaugas Mozūras
Mindaugas Mozūras

Reputation: 1461

Checking if element doesn't have attributes and doesn't have elements is not enough. You need to check if an element is truly empty (absolutely no content). XElement has a property, that actually helps you do that - XElement.IsEmpty.

var document = XDocument.Parse(@"<body><ul><li><a href='#'>Joy</a></li></ul><p>Hi.</p></body>");
document.Descendants("a").Remove();

var emptyElements = from element in document.Descendants()
                    where element.IsEmpty
                    select element;

while (emptyElements.Any())
    emptyElements.Remove();

Upvotes: 10

David
David

Reputation: 19707

The best I could come up with was...

var emptyElements = 
    from element in document.Descendants()
    where !element.Attributes().Any() && !element.Elements().Any()
    select element;


while(emptyElements.Any())
    emptyElements.Remove();

Then I realized that was a bad idea, it was removing too much but I didn't take the time to figure out why.

Upvotes: 0

Related Questions