Laden...

XML-Daten dynamisch in Datei schreiben?

Erstellt von C3PO vor 8 Jahren Letzter Beitrag vor 8 Jahren 5.124 Views
C3PO Themenstarter:in
6 Beiträge seit 2015
vor 8 Jahren
XML-Daten dynamisch in Datei schreiben?

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

T
314 Beiträge seit 2013
vor 8 Jahren

Hi,

du nimmst die eingelesene Struktur und navigierst zur gewünschten Node. Dort gibt es dann entsprechend die Möglichkeit auch neue Elemente hinzuzufügen. (z.B. AddNode)

1.029 Beiträge seit 2010
vor 8 Jahren

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

3.003 Beiträge seit 2006
vor 8 Jahren

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)

T
2.224 Beiträge seit 2008
vor 8 Jahren

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.

D
985 Beiträge seit 2014
vor 8 Jahren

@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

3.003 Beiträge seit 2006
vor 8 Jahren

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)