2010-04-15 31 views
3

我有一個XDocument看起來類似於的XDocument改變所有的屬性名

<root> 
    <a> 
      <b foo="1" bar="2" /> 
      <b foo="3" bar="4" /> 
      <b foo="5" bar="6" /> 
      <b foo="7" bar="8" /> 
      <b foo="9" bar="10" /> 
    </a> 
</root> 

我希望更改屬性FOO別的東西,屬性欄到別的東西。我怎樣才能輕鬆做到這一點?我目前的版本(如下)堆棧溢出大文件,並有一個可怕的氣味。

 string dd=LoadedXDocument.ToString(); 
     foreach (var s in AttributeReplacements) 
      dd = dd.Replace(s.Old+"=", s.New+"="); 
+0

好問題(+1)。查看我的答案以獲得完整的XSLT解決方案 - 可能是最簡單的一種。 – 2010-04-15 13:01:57

回答

2

下面是一個完整XSLT溶液

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:my="my:reps" 
    exclude-result-prefixes="my" 
> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 

    <my:replacements> 
     <foo1 old="foo"/> 
     <bar1 old="bar"/> 
    </my:replacements> 

    <xsl:variable name="vReps" select= 
    "document('')/*/my:replacements/*"/> 

<xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="@*"> 
    <xsl:variable name="vRepNode" select= 
    "$vReps[@old = name(current())]"/> 

    <xsl:variable name="vName" select= 
    "name(current()[not($vRepNode)] | $vRepNode)"/> 

    <xsl:attribute name="{$vName}"> 
    <xsl:value-of select="."/> 
    </xsl:attribute> 
</xsl:template> 
</xsl:stylesheet> 

當這個變換所提供的XML文檔應用,所需的結果是產生

<root> 
    <a> 
     <b foo1="1" bar1="2"/> 
     <b foo1="3" bar1="4"/> 
     <b foo1="5" bar1="6"/> 
     <b foo1="7" bar1="8"/> 
     <b foo1="9" bar1="10"/> 
    </a> 
</root> 

待辦事項這是一個通用的解決方案,允許指定和修改任何替換列表而無需修改代碼。替換可以在單獨的XML文件中,以便維護。

+0

非常感謝您的努力。 – Dested 2010-04-15 17:47:44

3

與文本搜索和替換這樣做應該使用StringBuilder,以避免在一個循環(大量的垃圾)創建的字符串的正常問題來實現。它也很難防止誤報(如果文本相匹配的屬性文本節點中出現?)

更好的選擇,有不同的權衡包括:

  1. 加載到的XDocument或XmlDocument的,通過迭代樹替換匹配的屬性。
  2. 使用XSLT
  3. 從XmlReader中讀取並直接寫入XmlWriter,並更改屬性。

這些#3避免了將整個文檔加載到內存中。 #2需要XSLT技能,但很容易允許任意數量的替換(XSLT的核心可以是模板,在運行時注入新的舊屬性對)。 #1可能是最簡單的,但整個文檔在內存中,以及處理多個替換的開銷。

我很可能會將XSLT與Xml Reader/Writer方法作爲備份。

然而#1應該最簡單的實現,像(忽略除其他細節XML命名空間):

using System.Xml.Linq; 
using System.Xml.XPath; 

var xdoc = XDocument.Load(....); 
var nav = xdoc.CreateNavigator(); 

foreach (repl in replacements) { 
    var found = (XPathNodeIterator) nav.Evaluate("//@" + repl.OldName); 

    while (found.MoveNext()) { 
    var node = found.Current; 
    var val = node.Value; 
    node.DeleteSelf(); // Moves ref to parent. 
    node.CreateAttribute("", repl.NewName, "", val); 
    } 
} 

最終的選擇將取決於與平衡性能(尤其是內存如果使用大型文檔的工作)和複雜性。但只有你(和你的團隊)可以打這個電話。