2013-02-25 69 views
1

考慮下面的XML:如何替換System.Xml.XmlDocument中的節點?

<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

任何你不使用[LINQ-to-XML](http://msdn.microsoft.com/zh-cn/library/bb387098.aspx)的原因?使用LINQ到XML這將是兩行代碼。 – dtb 2013-02-25 15:23:12

+0

我正在研究現有的程序/框架。這就是爲什麼格式是XmlDocument。 – 2013-02-25 15:51:35

回答

0

看起來像我發現使用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"); 
} 

的想法是:通過所有p節點

  1. 看。
  2. 循環遍歷所有子節點p
  3. 將它們添加到正確的位置。
  4. 在每個p節點後添加一箇中斷。
  5. 刪除p元素。

該位置是相當棘手的發現。通過使用InsertAfterp作爲位置元素,需要將第一個子節點添加到p的父節點。但是下一個孩子需要在之前添加的孩子之後添加。解決方案:保存它的位置並使用它。

注意:在pElement.ChildNodes集合上使用for each迭代器將不起作用,因爲在移動了一半節點之後,迭代器決定完成該操作。看起來它使用某種排序而不是一組對象。

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> 

,然後你可以使用XslCompiledTransform類生成字符串或與輸出XML流將它應用到你的XmlDocument

+0

是的,我也考慮過XSLT,但它看起來像是用一部canon殺死一隻蒼蠅,因爲你基本上會切換到另一個域來解決問題。這是一個解決方案,但我想知道是否有其他解決方案可用。 – 2013-02-25 16:33:45

+0

是的,它是另一個域,但是正確的用於XML轉換。您可以直接操作'XmlDocument',但生成的代碼很難理解和修改。 – MiMo 2013-02-25 19:27:36

+0

的確如此。雖然XSLT將是一個很好的解決方案,但它可能是工作在XmlDocument上的速度要快一點。我已經設法創建了一些能夠完成這項工作的代碼......這是可能的,但不是非常簡單。 – 2013-02-26 09:53:48

0

如果我明白你想達到什麼樣的,你可以嘗試這樣的:

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

如果我沒有正確的瞭解,也許你可以發佈你想實現輸出。

+0

我需要替換'p'節點。在你的情況下,我只是添加'br'節點,而不是刪除'p'。 – 2013-02-25 17:54:50