2010-04-15 5 views
3

J'ai un XDocument qui ressemble àXDocument changer tous les noms d'attributs

<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> 

Je souhaite changer l'attribut foo à autre chose, et la barre d'attribut à autre chose. Comment puis-je facilement faire cela? Ma version actuelle (ci-dessous) déborde de documents volumineux et a une odeur désagréable.

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

Bonne question (+1). Voir ma réponse pour une solution XSLT complète - probablement l'une des plus simples possibles. –

Répondre

2

Voici une solution XSLT complète:

<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> 

Lorsque cette transformation est appliquée sur le document XML fourni, le résultat souhaité est produit:

<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> 

Prenez note qu'il s'agit d'une solution générique permettant de spécifier et de modifier n'importe quelle liste de remplacements sans modifier le code. Les remplacements peuvent être dans un fichier XML séparé, pour faciliter la maintenance.

+0

Merci beaucoup pour vos efforts. – Dested

3

Faire cela avec une recherche de texte et de remplacement doit être fait en utilisant StringBuilder pour éviter les problèmes normaux de la création de chaînes dans une boucle (beaucoup de déchets). Il est également très difficile d'éviter les faux positifs (si le texte correspondant de l'attribut se produit dans un nœud de texte?)

meilleures options, avec différents compromis comprennent:

  1. Charger dans XDocument ou XmlDocument, itérer le arbre remplaçant les attributs correspondants.
  2. Utiliser un XSLT
  3. Lire à partir d'un XmlReader et écrire directement dans un XmlWriter, avec des attributs modifiés.

De ces # 3 évite de charger le document entier en mémoire. # 2 requiert des compétences XSLT mais autorise facilement un nombre arbitraire de remplacements (le noyau du XSLT pourrait être un modèle, avec les nouvelles, anciennes paires d'attributs injectées lors de l'exécution). # 1 est susceptible d'être le plus simple, mais avec le document entier en mémoire, et les frais généraux de gestion de multiples remplacements.

Je regarderais probablement XSLT avec l'approche de Xml Reader/Writer comme sauvegarde.

Cependant # 1 devrait être plus simple à mettre en œuvre, quelque chose comme (en ignorant les espaces de noms XML entre autres détails):

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

Le choix final dépendra des performances d'équilibrage (en particulier la mémoire si vous travaillez avec de grands documents) et de la complexité . mais seulement vous (et votre équipe) pouvez faire cet appel.

Questions connexes