Hallo Zusammen.
ganz kurz zu mir selbst: Ich bin Neueinsteiger in C# und lerne/entwickle mit Microsoft Visual Studio Community 2015. In meiner Jugend (so ab 13 Jahren aufwärts) habe ich intensiv mit Visual Basic 5 und 6 programmiert, daher sind gewisse Grundkenntnisse über Programmierung vorhanden.
Ich stehe gerade vor einem Problem, bei dem ich auch nach intensiven googlen keine Lösung finde. Ich habe eine Klasse mit einer Funktion WritetoXML, damit möchte ich Applikationsweit Daten in eine Datei schreiben können.
In meinen Gedanken funktioniert das folgendermaßen:
Program toolbox = new Program();
private void frmMaterialDurchsuchen_FormClosing(object sender, FormClosingEventArgs e)
{
toolbox.WritetoXML(toolbox.ConfigFile, "Settings/Visuals/MaterialKatalog/sizeX", Convert.ToString(this.Width));
toolbox.WritetoXML(toolbox.ConfigFile, "Settings/Visuals/MaterialKatalog/sizeY", Convert.ToString(this.Height));
toolbox.WritetoXML(toolbox.ConfigFile, "Settings/Visuals/MaterialKatalog/posX", Convert.ToString(this.Left));
toolbox.WritetoXML(toolbox.ConfigFile, "Settings/Visuals/MaterialKatalog/posY", Convert.ToString(this.Top));
}
Anschließend soll in meiner XML-Datei folgendes stehen:
<?xml version="1.0" encoding="utf-8"?>
<Settings>
<Visuals>
<MaterialKatalog>
<sizeX>1225</sizeX>
<sizeY>580</sizeY>
<posX>250</posX>
<posY>350</posY>
</MaterialKatalog>
</Visuals>
</Settings>
Sofern die Elemente in der XML bereits vorhanden sind, lassen sich die Werte prima via XElement bearbeiten. Dazu nutze ich folgenden Code:
public bool WritetoXML(string file, string nodepath, object nodeValue)
{
if (!File.Exists(file))
return false;
try
{
XElement root = XElement.Load(@file);
//Variablen deklarieren
bool isChanged = false;
string[] split = nodepath.Split(Convert.ToChar("/")); //...splitte den Node-Pfad in einzelne Nodes und schreibe in Array
string parentNode = split[split.Length - 2].ToString();
string targetNode = split[split.Length - 1].ToString();
foreach (XElement n in root.Descendants().Elements())
{
if ((n.Name == targetNode) && (n.Parent.Name == parentNode)) //Doppelte Prüfung: Stimmen targetNode und parentNode überein?
{
n.Parent.SetElementValue(targetNode, nodeValue); //Wenn ja: Ändere Value von targetNode in nodeValue
isChanged = true; //setze FLAG, dass der Wert geändert wurde.
break; //Beende foreach-Schleife
}
}
if (!isChanged) //Wenn targetNode nicht geändert werden konnte (Node existiert nicht)
{
//Wie prüfen, ob ob der Pfad von root bis targetNode existiert?
//Und wenn nicht, fehlende Nodes erstellen
}
root.Save(@file); //Datei speichern
return true; //Funktion erfolgreich beenden
}
catch (Exception ex)
{
MessageBox.Show(null, ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
//throw;
return false;
}
}
Wie man anhand des Codes erkennen kann, fällt mir keine elegante Methode ein, wie ich den Pfad von "Settings" bis zu "sizeX" (als Beispiel) dynamisch auf Existenz prüfen und ggf. neu erstellen kann.
Wobei das Prüfen auf Existenz noch relativ einfach wäre. Ich habe eine Methode zum Auslesen von XML-Daten:
public string ReadfromXML(string Node, string file)
{
if (!File.Exists(file))
return "<error>";
try
{
XmlDocument doc = new XmlDocument();
doc.Load(file);
XmlNode node = doc.SelectSingleNode(Node);
return node.FirstChild.Value;
}
catch (Exception ex)
{
//MessageBox.Show("Fehler beim Auslesen der Konfigurationsdatei.\n\n" + ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
return "<error>";
}
}
Diese Funktion kann ich ja schritt für schritt nutzen, um die Existenz zu prüfen. Aber wie kann ich neue Nodes zu einem bestehenden XML-Gerüst hinzufügen?
Ich hoffe ihr könnt mir weiterhelfen! 👍
Hi,
naja - im Prinzip ist das recht einfach - ich würde das alles ein klein wenig mehr auftrennen in mehrere Methoden, damit du noch durchblickst...
Folgendes zumindest mal als kurzes Beispiel:
public void WritetoXML(string file, string nodepath, object nodeValue)
{
// load document
XElement xdoc = XElement.Load(file);
var xpath = nodepath.Split('/');
// get element to save to
XElement element = xdoc;
for(int i = 0; i < xpath.Length; i++)
{
element = GetElement(element, xpath[i]);
}
// save value if necessary
string strValue = Convert.ToString(nodeValue);
if (element.Value != strValue)
{
element.Value = strValue;
xdoc.Save(file);
}
}
private XElement GetElement(XElement parent, string node)
{
// get child element
var element = parent.Element(node);
// create child if it does not exist
if (element == null)
{
element = new XElement(node);
parent.Add(element);
}
return parent.Element(node);
}
LG
Diese Funktion kann ich ja schritt für schritt nutzen, um die Existenz zu prüfen. Aber wie kann ich neue Nodes zu einem bestehenden XML-Gerüst hinzufügen?
Zuallererst verwirrt mich die Tatsache, dass du einmal mit XElement (Linq2Xml) und einmal mit XmlNode (XmlDocument) arbeitest. Das sind zwei verschiedene Ansätze, um XML zu verarbeiten.
Zweitens verstehe ich nicht, wieso du dir die Mühe machst, den XML-Path von Hand auseinander zu nehmen und dann zu interpretieren. Dafür wäre XPath dann da.
Drittens, erstes Codestück: wenn du schon mit Linq2Xml arbeitest, dann nicht so:
foreach (XElement n in root.Descendants().Elements())
{
if ((n.Name == targetNode) && (n.Parent.Name == parentNode)) //Doppelte Prüfung: Stimmen targetNode und parentNode überein?
{
n.Parent.SetElementValue(targetNode, nodeValue); //Wenn ja: Ändere Value von targetNode in nodeValue
isChanged = true; //setze FLAG, dass der Wert geändert wurde.
break; //Beende foreach-Schleife
}
}
...sondern so (direkt im Browser geschrieben, ohne Gewähr auf typos):
var xDoc = XDocument.Load(fileName);
var targetNode = xDoc.Root.XPathSelectElements(path).FirstOrDefault();
if(targetNode == null) return;
targetNode.Value = newValue;
xDoc.Save(fileName);
Du solltest evtl. noch ein bisschen mit beiden Ansätzen (XDocument und XmlDocument) herumprobieren und dich dann für einen entscheiden. Und weg vom XPath, deine toolbox sollte gar nicht wissen, in welchem Pfad in deinem XML etwas gespeichert wird.
LaTino
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
Blöde Frage -> Wäre es nicht sinnvoller direkt über Xml Serialisierung zu reden?
Dann musst du nur eine entsprechendes Model programmieren was du per Xml sowohl schreiben als auch lesen kannst.
Dann musst du dich nicht selbst um das schreiben/lesen kümmern sondern nur noch das entsprechende Model per Serialisierung in eine Datei schreiben oder aus dieser lesen.
Die Werte des Models kannst du dann im Speicher halten per Static Referenz und dann ganz einfach schreiben.
Mit den entsprechenden Attributen kannst du dann auch die Klasse + die Eigenschaften in den entsprechenden Felder in der Xml Datei serialisieren.
T-Virus
Developer, Developer, Developer, Developer....
99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
@T-Virus 👍
Vor allem weil man das entsprechende Model mit 2 Klicks aus der XML-Datei bekommen kann.
Bearbeiten / Inhalte einfügen / XML als Klassen einfügen
Blöde Frage -> Wäre es nicht sinnvoller direkt über Xml Serialisierung zu reden?
Nee, gar nicht blöd 😄. Die Funktionalität des Serialisieren/Deserialisieren in ein eigenes Datenobjekt auslagern, diesem noch zB eine Property "DataProvider" geben, eine Schnittstelle für DataProvider definieren, und als Beispielimplementierung einen XmlDataProvider schreiben, der das vom Objekt intern gehaltene XML auch als XML-Datei speichert. Wenn man's dann doch in einer DB speichern will, braucht man nur einen DBDataProvider und ist fein raus.
Ging aber etwas über die ursprüngliche Frage hinaus.
LaTino
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)