删除没有子节点的父节点

war*_*cik 5 c# xml xpath linq-to-xml xml-parsing

我有一个与从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>
Run Code Online (Sandbox Code Playgroud)

我想删除节点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>
Run Code Online (Sandbox Code Playgroud)

我一直在尝试这样的代码来实现:

        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);
Run Code Online (Sandbox Code Playgroud)

我能够找到具有所需属性toRemove或placeHolder的节点,但是我无法将此节点的子节点向上移动一级.在这种情况下你能帮我吗?它可以是Linq,XDocument,XmlReader的解决方案,但我更喜欢使用XmlDocument.感谢您提前给我提供的任何帮助.

编辑:

在这种情况下,我使用了稍微修改过的代码(以保持顺序),Chuck Savage写道如下.一旦删除

  <nodeB attribute="toRemove"> </nodeB>
Run Code Online (Sandbox Code Playgroud)

然后再做同样的事情

  <nodeB attribute="placeHolder"></nodeB>
Run Code Online (Sandbox Code Playgroud)

这是稍微修改过的代码

  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);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,@ MiMo提供的xslt方法非常有用.

MiM*_*iMo 5

问题在于,枚举其子节点时不能修改文档节点-应该创建新节点,而不要尝试修改现有节点,并且使用会有些棘手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>
Run Code Online (Sandbox Code Playgroud)

输入文件的输出为:

<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>
Run Code Online (Sandbox Code Playgroud)

应用XSLT的代码很简单:

  XslCompiledTransform transform = new XslCompiledTransform();
  transform.Load(@"c:\temp\nodes.xslt");
  transform.Transform(@"c:\temp\nodes.xml", @"c:\temp\nodes-cleaned.xml");
Run Code Online (Sandbox Code Playgroud)

如果不可能(或不希望)将外部文件用于XSLT,则可以从字符串中读取它:

  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");    
Run Code Online (Sandbox Code Playgroud)


Chu*_*age 4

使用 Linq-to-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);
Run Code Online (Sandbox Code Playgroud)

注意:XPath 可用于System.Xml.XPath

注意2:您可以使用这些扩展与 XmlDocument 进行转换,因为您更喜欢 XmlDocument。