【问题标题】:How to replace nodes in System.Xml.XmlDocument?如何替换 System.Xml.XmlDocument 中的节点?
【发布时间】:2013-02-10 18:58:39
【问题描述】:

考虑以下 xml:

<div>
   <a href="http://www.google.com/">This is:</a>
   <p>A test... <b>1</b><i>2</i><u>3</u></p>
   <p>This too</p>
   Finished.
</div>

此 xml 的内容位于 System.Xml.XmlDocument 实例中。我需要替换所有 p 元素并在每个段落元素后添加一个中断。我写了以下代码:

var pElement = xmlDocument.SelectSingleNode("//p");
while (pElement != null)
{
    var textNode = xmlDocument.CreateTextNode("");
    foreach (XmlNode child in pElement.ChildNodes)
    {
        textNode.AppendChild(child);
    }    
    textNode.AppendChild(xmlDocument.CreateElement("br"));
    pElement.ParentNode.ReplaceChild(pElement, textNode);
    pElement = xmlDocument.SelectSingleNode("//p");
}

我正在创建一个空节点并将每个段落节点的子节点添加到它。不幸的是,这不起作用:文本节点不能包含元素。

任何想法如何实现此替换?

【问题讨论】:

  • 有什么理由不使用LINQ-to-XML?对于 LINQ-to-XML,这将是两行代码。
  • 我正在开发一个现有的程序/框架。这就是格式是 XmlDocument 的原因。

标签: c# asp.net xmldocument


【解决方案1】:

看来我找到了使用InsertAfter 方法的解决方案:

var pElement = xmlDocument.SelectSingleNode("//p");

while (pElement != null)
{    
    //store position where new elements need to be added
    var position = pElement;

    while(pElement.FirstChild != null)
    {
        var child = pElement.FirstChild;
        position.ParentNode.InsertAfter(child, position);

        //store the added child as position for next child
        position = child;
    }

    //add break
    position.ParentNode.InsertAfter(xmlDocument.CreateElement("br"), position);

    //remove empty p
    pElement.ParentNode.RemoveChild(pElement);

    //select next p
    pElement = xmlDocument.SelectSingleNode("//p");
}

思路如下:

  1. 查看所有p 节点。
  2. 循环遍历p的所有子节点。
  3. 将它们添加到正确的位置。
  4. 在每个 p 节点之后添加一个中断。
  5. 删除p 元素。

这个位置很难找到。第一个子节点需要使用InsertAfterp 作为位置元素添加到p 的父节点。但是下一个孩子需要在之前添加的孩子之后添加。解决方法:保存它的位置并使用它。

注意:在pElement.ChildNodes 集合上使用for each 迭代器将不起作用,因为在移动一半节点后,迭代器决定它已完成。看起来它使用某种计数而不是对象集合。

【讨论】:

    【解决方案2】:

    修改内存中的 XmlDocument 对象是有问题的 - 请参阅类似的问题 here

    最简单的方法是使用 XSLT:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
      <xsl:output method="xml" indent="yes"/>
    
      <xsl:template match="p">
        <xsl:copy-of select="node()"/>
        <br/>
      </xsl:template>
    
      <xsl:template match="@* | node()">
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:template>
    
    </xsl:stylesheet>
    

    然后您可以使用XslCompiledTransform 类将其应用于您的XmlDocument,生成带有输出XML 的字符串或流。

    【讨论】:

    • 是的,我也考虑过 XSLT,但它看起来像用佳能杀死苍蝇,因为您基本上是切换到另一个域来解决问题。这是一个解决方案,但我想知道是否还有其他解决方案。
    • 是的,它是另一个域,但可以进行 XML 转换。可以直接操作XmlDocument,但是生成的代码很难理解和修改。
    • 确实如此。尽管 XSLT 将是一个很好的解决方案,但在 XmlDocument 上工作可能会快一点。我已经设法创建了一些代码来完成这项工作......这是可能的,但不是很直接。
    【解决方案3】:

    如果我理解您想要实现的目标,您可以尝试以下方法:

    foreach (XmlNode node in doc.SelectNodes("//p"))
    {
        node.ParentNode.InsertAfter(doc.CreateElement("br"), node);
    }
    

    如果我没有理解正确,也许你可以发布你想要实现的输出。

    【讨论】:

    • 我需要替换 p 节点。在您的情况下,我只添加 br 节点,而不是删除 p's。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-16
    • 2012-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多