XML:如何删除它们没有属性也没有
我有一个XML文档,像这样子元素的所有节点:XML:如何删除它们没有属性也没有
<Node1 attrib1="abc">
<node1_1>
<node1_1_1 attrib2 = "xyz" />
</ node1_1>
</Node1>
<Node2 />
这里<node2 />
是我想删除,因为它有没有儿童/元素,也不是任何一个节点属性。
使用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();
谢谢,这对我来说工作得很好:) – mishal153 2010-04-01 07:53:15
只是想添加一件事。我意识到我还需要涵盖节点类似于
你可以通过使用'node()'结合'*'和'text()'的测试并使用联合'|'来合并对count和criteria的属性和节点的测试来简化XPATH表达式:'//* [count(child :: node()| @ *)= 0]' – 2010-04-01 13:33:35
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;
}
}
}
}
我提到的节点名称只是为了解释我想要的。它们不是真正的节点名称。我想做一些通用的事情。我相信XPath在这里很有用,但我不知道如何使用XPath。我正在阅读有关:)。感谢您的回复。 – mishal153 2010-04-01 07:42:01
这个样式表使用一个身份与空模板匹配的元素,而节点或属性,这将防止它们被复制到输出转换:
<?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方法的复制文档处理。更重要的是,代码更易读,更容易编辑。双赢。
因此,此解决方案将删除<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秒。
如果node1_1_1被删除,node1_1也被删除,因为它不会有任何子/元素或任何属性? – 2010-04-01 07:32:27
好问题。我的错在这里,,,不,只有 需要删除。其实真正的情况是这样的: node1_1> 所以 是需要被移除的一个。 我已经更新了这个问题。 –
mishal153
2010-04-01 07:45:35