如何替换System.Xml.XmlDocument中的节点?

时间:2013-02-25 15:19:29

标签: c# asp.net xmldocument

考虑以下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");
}

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

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

3 个答案:

答案 0 :(得分: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. 将它们添加到正确的位置。
  5. 在每个p节点后添加一个中断。
  6. 删除p元素。
  7. 找到这个位置非常棘手。需要使用p InsertAfter作为位置元素,将第一个子节点添加到p的父节点。但是下一个孩子需要在之前添加的孩子之后添加。解决方案:保存其位置并使用它。

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

答案 1 :(得分:0)

修改内存中的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>

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

答案 2 :(得分:0)

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

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

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