代码之家  ›  专栏  ›  技术社区  ›  T. Stone

xdocument更改所有属性名

  •  3
  • T. Stone  · 技术社区  · 14 年前

    我有一个类似于

    <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+"=");
    
    2 回复  |  直到 14 年前
        1
  •  2
  •   Dimitre Novatchev    14 年前

    这里有一个完整的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文件中,以便于维护。

        2
  •  3
  •   Richard    14 年前

    使用文本搜索和替换进行此操作应使用 StringBuilder 为了避免在循环中创建字符串的正常问题(大量垃圾)。也很难防止误报(如果文本节点中出现与属性匹配的文本怎么办?)

    更好的选择,包括:

    1. 加载到xdocument或xmldocument中,迭代树以替换匹配的属性。
    2. 使用XSLT
    3. 从xmlReader读取并直接写入具有更改属性的xmlWriter。

    其中3避免将整个文档加载到内存中。#2需要XSLT技能,但很容易允许任意数量的替换(XSLT的核心可以是一个模板,在运行时注入新的、旧的属性对)。#1可能是最简单的,但是整个文档都在内存中,处理多个替换的开销也很大。

    我可能会将使用XML读写器方法的XSLT作为备份。

    但是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);
      }
    }
    

    最终的选择将取决于平衡性能(尤其是处理大型文档时的内存)和复杂性。但只有你(和你的团队)才能打电话。