2010-04-01 55 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秒。