删除无子节点的父节点
我有一个问题涉及从XML文件中删除特定的节点。删除无子节点的父节点
这里是我的XML的示例:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<nodeA attribute="1">
<nodeB attribute="table">
<nodeC attribute="500"></nodeC>
<nodeC attribute="5"></nodeC>
</nodeB>
<nodeB attribute="3">
<nodeC attribute="4"></nodeC>
<nodeC attribute="5"></nodeC>
<nodeC attribute="5"></nodeC>
</nodeB>
<nodeB attribute="placeHolder">
<nodeB attribute="toRemove">
<nodeB attribute="glass"></nodeB>
<nodeE attribute="7"></nodeE>
<nodeB attribute="glass"></nodeB>
<nodeB attribute="glass"></nodeB>
</nodeB>
</nodeB>
<nodeB attribute="3">
<nodeC attribute="4"></nodeC>
<nodeC attribute="5"></nodeC>
<nodeC attribtue="5"></nodeC>
</nodeB>
<nodeB attribute="placeHolder">
<nodeB attribute="toRemove">
<nodeB attribute="glass"></nodeB>
<nodeE attribute="7"></nodeE>
<nodeB attribute="glass"></nodeB>
<nodeB attribute="glass"></nodeB>
</nodeB>
</nodeB>
</nodeA>
</root>
我想删除节点nodeB="toRemove"
,不删除此节点的儿童。之后我需要用nodeB attribute="placeHolder"
做同样的事情。结果的部分看起来像:
<nodeB attribute="3">
<nodeC attribute="4"></nodeC>
<nodeC attribute="5"></nodeC>
<nodeC attribtue="5"></nodeC>
</nodeB>
<nodeB attribute="glass"></nodeB>
<nodeE attribute="7"></nodeE>
<nodeB attribute="glass"></nodeB>
<nodeB attribute="glass"></nodeB>
我一直是这样想的代码才达到的是:
XmlNodeList nodeList = doc.SelectNodes("//nodeB[@attribute=\"toRemove\"]");
foreach (XmlNode node in nodeList)
{
foreach (XmlNode child in node.ChildNodes)
{
node.ParentNode.AppendChild(child);
}
node.ParentNode.RemoveChild(node);
}
doc.Save(XmlFilePathSource);
我能够找到节点与期望的属性的文档,删除或占位符,但我不是能够将此节点的子节点向上移动一级。在这种情况下你能帮助我吗?它可以与Linq,XDocument,XmlReader解决,但我更喜欢使用XmlDocument。 感谢您提前给我提供的任何帮助。
编辑:
在这种情况下,我使用了略微修改后的代码(保存顺序)查野人写道波纹管。一旦删除
<nodeB attribute="toRemove"> </nodeB>
,然后做同样的
<nodeB attribute="placeHolder"></nodeB>
这里稍微修改后的代码由@MiMo提供
XElement root = XElement.Load(XmlFilePathSource);
var removes = root.XPathSelectElements("//nodeB[@attribute=\"toRemove\"]");
foreach (XElement node in removes.ToArray())
{
node.Parent.AddAfterSelf(node.Elements());
node.Remove();
}
root.Save(XmlFilePathSource);
XSLT方法是非常有用的,以及在这种情况下。
使用LINQ到XML和你的XPath,
XElement root = XElement.Load(XmlFilePathSource); // or .Parse(string)
var removes = root.XPathSelectElements("//nodeB[@attribute=\"toRemove\"]");
foreach (XElement node in removes.ToArray())
{
node.AddBeforeSelf(node.Elements());
node.Remove();
}
root.Save(XmlFilePathSource);
注:XPath是在System.Xml.XPath
注2可供选择:你可以转换到/从XmlDocument使用these extensions,因为您更喜欢XmlDocument。
问题是,您不能在枚举子节点时修改文档节点 - 您应该创建新节点而不是尝试修改现有节点,而使用XmlDocument
会变得有点棘手。
做这种转换的最简单的方法是使用XSLT,即应用该XSLT:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="nodeB[@attribute='toRemove' or @attribute='placeHolder']">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()">
</xsl:template>
<xsl:template match="@* | *">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
输入文件输出为:
<root>
<nodeA attribute="1">
<nodeB attribute="table">
<nodeC attribute="500" />
<nodeC attribute="5" />
</nodeB>
<nodeB attribute="3">
<nodeC attribute="4" />
<nodeC attribute="5" />
<nodeC attribute="5" />
</nodeB>
<nodeB attribute="glass" />
<nodeE attribute="7" />
<nodeB attribute="glass" />
<nodeB attribute="glass" />
<nodeB attribute="3">
<nodeC attribute="4" />
<nodeC attribute="5" />
<nodeC attribtue="5" />
</nodeB>
<nodeB attribute="glass" />
<nodeE attribute="7" />
<nodeB attribute="glass" />
<nodeB attribute="glass" />
</nodeA>
</root>
应用XSLT代码简单地说就是:
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(@"c:\temp\nodes.xslt");
transform.Transform(@"c:\temp\nodes.xml", @"c:\temp\nodes-cleaned.xml");
如果不可能(或可取的)使用X的外部文件SLT它可以从一个字符串读取:
string xsltString =
@"<xsl:stylesheet
version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method=""xml"" indent=""yes""/>
<xsl:template match=""nodeB[@attribute='toRemove' or @attribute='placeHolder']"">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match=""text()"">
</xsl:template>
<xsl:template match=""@* | *"">
<xsl:copy>
<xsl:apply-templates select=""@* | node()""/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>";
XslCompiledTransform transform = new XslCompiledTransform();
using (StringReader stringReader = new StringReader(xsltString))
using (XmlReader reader = XmlReader.Create(stringReader)) {
transform.Load(reader);
}
transform.Transform(@"c:\temp\nodes.xml", @"c:\temp\nodes-cleaned.xml");
感谢您的答案。当我能够加载其他文件时,我会再次使用这种方法。但是,在这种特殊情况下,我无法使用外部文件。所以在我的情况下加载xslt文件不是一个选项。 – wariacik 2013-02-20 17:38:08
@wariacik:即使没有外部文件,您仍然可以使用XSLT - 我扩展了我的答案。 XSLT的问题在于如果你不熟悉它们,它们很难使用 - 但是如果你做了大量的XML处理,学习它们是一个很好的投资。 – MiMo 2013-02-20 19:11:17
谢谢。我不知道我可以加载xslt作为一个字符串。这对我的项目非常有用。 – wariacik 2013-02-20 19:35:11
我知道它的一个老问题,但我直接使用XmlDocument写了这个。
添加,如果有人喜欢做这种方式:
XmlNode child_to_remove = parent.ChildNodes[i]; // get the child to remove
// move all the children of "child_to_remove" to be the child of their grandfather (== parent)
while(child_to_remove.HasChildNodes)
parent.InsertBefore(child_to_remove.ChildNodes[0], child_to_remove);
parent.RemoveChild(child_to_remove);
这:-)就是它,希望它会帮助任何人。
您的许多'nodeC'元素缺少结束标记。你能用有效的,格式良好的XML更新你的问题吗? – 2013-02-19 21:26:49
我已经更新了我的简化的xml文件。感谢提示,现在更容易为其他人阅读。 – wariacik 2013-02-19 23:04:35