XML: как удалить все узлы, которые не имеют ни атрибутов, ни дочерних элементов - PullRequest
4 голосов
/ 01 апреля 2010

У меня есть XML-документ, подобный этому:

<Node1 attrib1="abc">
    <node1_1>
         <node1_1_1 attrib2 = "xyz" />
    </ node1_1>
</Node1>

<Node2 />    

Здесь <node2 /> - это узел, который я хочу удалить, так как у него нет ни дочерних элементов, ни каких-либо атрибутов.

Ответы [ 4 ]

4 голосов
/ 01 апреля 2010

Используя выражение XPath, можно найти все узлы, которые не имеют атрибутов или дочерних элементов. Затем они могут быть удалены из XML. Как указывает Сани, вам, возможно, придется делать это рекурсивно, потому что node_1_1 станет пустым, если вы удалите его внутренний узел.

var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(
@"<Node1 attrib1=""abc"">
        <node1_1>
             <node1_1_1 />
        </node1_1>
    </Node1>
    ");

// select all nodes without attributes and without children
var nodes = xmlDocument.SelectNodes("//*[count(@*) = 0 and count(child::*) = 0]");

Console.WriteLine("Found {0} empty nodes", nodes.Count);

// now remove matched nodes from their parent
foreach(XmlNode node in nodes)
    node.ParentNode.RemoveChild(node);

Console.WriteLine(xmlDocument.OuterXml);
Console.ReadLine();
1 голос
/ 01 апреля 2010

Что-то вроде этого должно сделать это:

XmlNodeList nodes = xmlDocument.GetElementsByTagName("Node1");

foreach(XmlNode node in nodes)
{
    if(node.ChildNodes.Count == 0)
         node.RemoveAll;
    else
    {
        foreach (XmlNode n in node)
        {
            if(n.InnerText==String.Empty && n.Attributes.Count == 0)
            {
                n.RemoveAll;

            }
        }
    }
}
0 голосов
/ 29 июля 2015

Чтобы сделать это для всех пустых дочерних узлов, используйте цикл for (не foreach) и в обратном порядке. Я решил это как:

var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(@"<node1 attrib1=""abc"">
                         <node1_1>
                            <node1_1_1 />
                         </node1_1>
                         <node1_2 />
                         <node1_3 />
                      </node1>
                      <node2 />
");
RemoveEmptyNodes(xmlDocument );

private static bool RemoveEmptyNodes(XmlNode node)
{
    if (node.HasChildNodes)
    {
        for(int I = node.ChildNodes.Count-1;I >= 0;I--)
            if (RemoveEmptyNodes(node.ChildNodes[I]))
                node.RemoveChild(node.ChildNodes[I]);
    }
    return 
        (node.Attributes == null || 
            node.Attributes.Count == 0) && 
        node.InnerText.Trim() == string.Empty;
}

Рекурсивные вызовы (аналогично другим решениям) исключают обработку дублированных документов в подходе xPath. Что еще более важно код более читабелен и более готов к редактированию. Win-Win.

Итак, это решение удалит <node2>, но также правильно удалит <node1_2> и <node1_3>.

Обновление: Обнаружено заметное увеличение производительности благодаря использованию следующей реализации Linq.

string myXml = @"<node1 attrib1=""abc"">
                         <node1_1>
                            <node1_1_1 />
                         </node1_1>
                         <node1_2 />
                         <node1_3 />
                      </node1>
                      <node2 />
");
XElement xElem = XElement.Parse(myXml);
RemoveEmptyNodes2(xElem);

private static void RemoveEmptyNodes2(XElement elem)
{
    int cntElems = elem.Descendants().Count();
    int cntPrev;
    do
    {
        cntPrev = cntElems;
        elem.Descendants()
            .Where(e => 
                string.IsNullOrEmpty(e.Value.Trim()) && 
                !e.HasAttributes).Remove();
        cntElems = elem.Descendants().Count();
    } while (cntPrev != cntElems);
}

Цикл обрабатывает случаи, когда родительский элемент должен быть удален, поскольку был удален только его дочерний элемент. Использование XContainer или производных имеет тенденцию к аналогичному увеличению производительности из-за реализаций IEnumerable за кулисами. Это моя новая любимая вещь.

В произвольном 68-мегабайтном XML-файле RemoveEmptyNodes обычно занимает около 90 с, а RemoveEmptyNodes2 - около 1 с.

0 голосов
/ 01 апреля 2010

Эта таблица стилей использует преобразование идентичности с пустым шаблоном, соответствующим элементам без узлов или атрибутов, что предотвратит их копирование в вывод:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <!--Identity transform copies all items by default -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <!--Empty template to match on elements without attributes or child nodes to prevent it from being copied to output -->
    <xsl:template match="*[not(child::node() | @*)]"/>

</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...