J'essaie de trouver le meilleur moyen de changer la valeur d'un élément en XML.
<MyXmlType>
<MyXmlElement>Value</MyXmlElement>
</MyXmlType>
Quel est le moyen le plus facile et/ou le meilleur de changer "Valeur" en C #?
J'ai examiné XMLDocument et cela entraînera un chargement complet du document XML en mémoire. Pourriez-vous le faire en toute sécurité avec XMLReader. Le problème est de changer la valeur et de la restituer semble être une énigme intéressante.
A la vôtre: D
Vous pouvez utiliser l'espace de noms System.Xml.Linq pour le code le plus facile à lire. Cela chargera le fichier complet en mémoire.
XDocument xdoc = XDocument.Load("file.xml");
var element = xdoc.Elements("MyXmlElement").Single();
element.Value = "foo";
xdoc.Save("file.xml");
EDIT: n'a pas vu votre clause sur XmlDocument. XmlReader fait justement cela. Vous ne pouvez pas éditer des fichiers XML avec cette classe.
Vous voulez XmlWriter. Cependant, au cas où cela vous aiderait, voici le code pour XmlDocument.
private void changeXMLVal(string element, string value)
{
try
{
string fileLoc = "PATH_TO_XML_FILE";
XmlDocument doc = new XmlDocument();
doc.Load(fileLoc);
XmlNode node = doc.SelectSingleNode("/MyXmlType/" + element);
if (node != null)
{
node.InnerText = value;
}
else
{
XmlNode root = doc.DocumentElement;
XmlElement elem;
elem = doc.CreateElement(element);
elem.InnerText = value;
root.AppendChild(elem);
}
doc.Save(fileLoc);
doc = null;
}
catch (Exception)
{
/*
* Possible Exceptions:
* System.ArgumentException
* System.ArgumentNullException
* System.InvalidOperationException
* System.IO.DirectoryNotFoundException
* System.IO.FileNotFoundException
* System.IO.IOException
* System.IO.PathTooLongException
* System.NotSupportedException
* System.Security.SecurityException
* System.UnauthorizedAccessException
* System.UriFormatException
* System.Xml.XmlException
* System.Xml.XPath.XPathException
*/
}
}
Utiliser un lecteur avant uniquement va certainement être l'approche la plus efficace. Dans ce cas, une dérivation XmlReader semble appropriée, bien que cela demande encore plus de travail que d'utiliser une approche DOM pour charger le fichier entier en une fois.
XmlReader est censé être une amélioration par rapport à l’API de l’analyseur XML SAX, issu du monde Java, mais qui est devenu un standard de facto dans le secteur (en dehors de Microsoft).
Si vous souhaitez simplement que le travail soit effectué rapidement, XmlTextReader existe à cet effet (dans .NET).
Si vous souhaitez apprendre un standard de facto stable (et également disponible dans de nombreux langages de programmation) et qui vous obligera à coder de manière très efficace et élégante, mais qui est également extrêmement flexible, consultez SAX. Cependant, ne vous préoccupez pas de SAX, sauf si vous allez créer des analyseurs XML hautement ésotériques. De nombreux analyseurs utilisent actuellement SAX sous la couverture.
S'il vous plaît vérifier ma réponse à propos de SAX ici pour une liste des ressources SAX et une idée vraiment créative .NET XML qui utilise XmlTextReader comme base: SAX vs XmlTextReader - SAX en C #
Vous pouvez utiliser un XmlReader pour lire dans une classe qui extrait les données via un XmlWriter et rechercher l'élément entre les valeurs lecture/écriture, en modifiant la valeur si nécessaire.
Honnêtement, je suis un peu surpris que votre fichier XML soit si énorme que vous vous inquiétiez de la consommation de mémoire ... ne dites pas que ce n'est jamais un problème. Sans plus d'informations, je ne peux pas dire que votre fichier XML hypothétique ne pèse pas 50 Go, mais dans de nombreux cas, le chargement de fichiers qui semblent assez volumineux en mémoire suffisamment longtemps pour être manipulés n'est pas un problème aussi important que vous ne le pensez.
Avez-vous pensé à utiliser Linq to XML? (si vous utilisez .Net 3.0+)
public static XElement ChangeValue(string xmlSnippet,
string nodeName,
string newValue)
{
XElement snippet = XElement.Parse(xmlSnippet);
if (snippet != null)
{
snippet.Element(nodeName).Value = newValue;
}
return snippet;
}
J'imagine que XElement fonctionnera mieux que XmlDocument (bien que je ne sois pas sûr), l'objet de base de XElement est XObject et, oui, il devra charger tout le document.
Cela utilise un ancien fichier et en crée un nouveau avec une valeur mise à jour. Il lève une exception s'il ne peut pas trouver l'élément
{
XDocument newSettingFile = new XDocument(settingFile);
//Root element
var newSetting = newSettingFile.Element("MyXmlType");
//Update childelement with new value
newSetting.Element("MyXmlElement").Value = "NewValue";
return newSettingFile;
}
using System;
using System.Xml;
using System.Linq;
using System.Xml.Linq;
namespace ReandAndWriteXML
{
class MainClass
{
public static void Main (string[] args)
{
XDocument xdoc = XDocument.Load(@"file.xml");
var element = xdoc.Root.Elements("MyXmlElement").Single();
element.Value = "This wasn't nearly as hard as the internet tried to make it!";
xdoc.Save(@"file.xml");
}
}
}
Cela ressemble beaucoup à l'exemple de Ben Robin, sauf que ça marche (bien que le sien aussi, maintenant qu'il a été édité). Et je vous ai même donné les directives d'utilisation!
J'ai effectué des tests sur un document de 10,6 K. L'analyse de XmlDocument est toujours plus rapide que la requête Linq, d'environ 50%.
var stopwatch2 = Stopwatch.StartNew();
XmlDocument xd = new XmlDocument();
xd.LoadXml(instanceXML);
XmlNode node = xd.SelectSingleNode("//figures/figure[@id='" + stepId + "']/properties/property[@name='" + fieldData + "']");
node.InnerXml = "<![CDATA[ " + valData + " ]]>";
stopwatch2.Stop();
var xmlDocTicks = stopwatch2.ElapsedTicks;
Stopwatch stopwatch1 = Stopwatch.StartNew();
XDocument doc = XDocument.Parse(instanceXML);
XElement prop =
(from el in doc.Descendants("figure")
where (string)el.Attribute("id") == stepId
select el).FirstOrDefault();
prop.Value = valData;
stopwatch1.Stop();
var linqTicks = stopwatch1.ElapsedTicks;
Les résultats sont les suivants (xmlDocTicks, linqTicks):
Charger et sauvegarder
public XDocument XDocument { get; set; }
private async Task OpenResWFileAsync()
{
List<XElement> dataElements;
var reswFile = await StorageHelper.PickSingleFileAsync(".resw");
if (reswFile == null) return;
using (Stream fileStream = await reswFile.OpenStreamForReadAsync())
{
this.XDocument = XDocument.Load(fileStream);
dataElements = this.XDocument.Root.Elements("data").ToList();
this.DataElements = dataElements;
}
}
#region
private List<string> GetValues()
{
if (this.XDocument == null) return new List<string>();
return this.XDocument.Root.Elements("data").Select(e => e.Attribute("name").Value).ToList();
}
public void ChangeValue(string resourceKey, string newValue)
{
if (this.DataElements == null) return;
var element = this.DataElements.Where(e => e.Name == resourceKey).Single();
element.Value = newValue;
}
#endregion