Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# XML:如何删除没有属性或子元素的所有节点_C#_Xml - Fatal编程技术网

C# XML:如何删除没有属性或子元素的所有节点

C# XML:如何删除没有属性或子元素的所有节点,c#,xml,C#,Xml,我有这样一个xml文档: <Node1 attrib1="abc"> <node1_1> <node1_1_1 attrib2 = "xyz" /> </ node1_1> </Node1> <Node2 /> 这是我要删除的节点,因为它既没有子节点/元素,也没有任何属性 像这样的事情应该可以做到: XmlNodeList nodes = xmlDocument.GetEleme

我有这样一个xml文档:

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

<Node2 />    

这是我要删除的节点,因为它既没有子节点/元素,也没有任何属性

像这样的事情应该可以做到:

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;

            }
        }
    }
}

使用XPath表达式可以找到所有没有属性或子节点的节点。然后可以从xml中删除这些内容。正如萨尼指出的,您可能必须递归地执行此操作,因为如果删除节点的内部节点,节点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();

此样式表使用带有空模板的标识转换来匹配没有节点或属性的元素,这将阻止将它们复制到输出:

<?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>

要对所有空的子节点执行此操作,请使用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方法的重复文档处理。更重要的是,代码更具可读性,更易于编辑。双赢

因此,此解决方案将删除,但也会正确删除和

更新:发现通过使用以下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);
}
循环处理由于父项的唯一子项被删除而需要删除父项的情况。由于IEnumerable的幕后实现,使用XContainer或其衍生物往往会有类似的性能提升。这是我最喜欢的新东西


在任意68MB xml文件上,RemoveEmptyNodes往往需要90秒左右,RemoveEmptyNodes 2则需要1秒左右。

如果删除节点1_1_1,是否也应该删除节点1_1,因为这样它就不会有任何子元素或任何属性?好问题。我错了,不,只需要删除。事实上,真正的场景是这样的:需要删除的场景也是这样。我已经更新了问题。我提到的节点名称只是为了解释我想要什么。它们不是真正的节点名。我想做些普通的事情。我相信XPath在这里会很有用,但我不知道如何使用XPath。我正在读有关它的书:。谢谢你的回复。只想补充一件事。我意识到我还需要讨论节点类似于hello的情况。在这里,节点没有子节点,也没有属性,但它有文本,因此我不希望对其进行筛选和删除。所以对我来说正确的解决方案是:XmlNodeList=document.SelectNodes/*[count@*=0和countchild::*=0和nottext];您可以通过使用node来组合*和文本的测试,并使用union |来合并计数条件的属性和节点的测试://*[countchild::node |@*=0]来简化该XPATH表达式