2010-04-01 57 views
4

我有一个XML文档,像这样子元素的所有节点:XML:如何删除它们没有属性也没有

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

<Node2 />  

这里<node2 />是我想删除,因为它有没有儿童/元素,也不是任何一个节点属性。

+1

如果node1_1_1被删除,node1_1也被删除,因为它不会有任何子/元素或任何属性? – 2010-04-01 07:32:27

+0

好问题。我的错在这里,,,不,只有需要删除。其实真正的情况是这样的: <节点1 attrib1 = “ABC”> 所以是需要被移除的一个。 我已经更新了这个问题。 – mishal153 2010-04-01 07:45:35

回答

4

使用XPath表达式,可以发现,没有属性或孩子的所有节点。这些可以从XML中删除。正如Sani指出的那样,您可能必须递归执行此操作,因为如果删除其内部节点,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(); 
+0

谢谢,这对我来说工作得很好:) – mishal153 2010-04-01 07:53:15

+1

只是想添加一件事。我意识到我还需要涵盖节点类似于 hello的情况。这里的节点没有孩子也没有属性,但它有文本,所以我不希望它被过滤和删除。所以对我来说正确的解决方案是: XmlNodeList list = document.SelectNodes(“// * [count(@ *)= 0 and count(child :: *)= 0 and not(text())]”); – mishal153 2010-04-01 10:09:46

+3

你可以通过使用'node()'结合'*'和'text()'的测试并使用联合'|'来合并对count和criteria的属性和节点的测试来简化XPATH表达式:'//* [count(child :: node()| @ *)= 0]' – 2010-04-01 13:33:35

1

Smething这样应该这样做:

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

我提到的节点名称只是为了解释我想要的。它们不是真正的节点名称。我想做一些通用的事情。我相信XPath在这里很有用,但我不知道如何使用XPath。我正在阅读有关:)。感谢您的回复。 – mishal153 2010-04-01 07:42:01

0

这个样式表使用一个身份与空模板匹配的元素,而节点或属性,这将防止它们被复制到输出转换:

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

要为所有空的子节点做到这一点,使用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方法的复制文档处理。更重要的是,代码更易读,更容易编辑。双赢。

因此,此解决方案将删除<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的实现而具有类似的性能提升。这是我最喜欢的事情。

在一个任意的68MB xml文件上RemoveEmptyNodes往往需要大约90秒,而RemoveEmptyNodes2往往需要大约1秒。