2010-04-15 3 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 - возможно, одного из самых простых. –

ответ

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

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

Окончательный выбор будет зависеть от балансировки производительности (особенно памяти при работе с большими документами) и сложность , но только вы (и ваша команда) можете сделать этот звонок.

Смежные вопросы