简体   繁体   中英

XMLDocument: Root element is missing

I'm working on a program to manage all customers we have in our company. I want to use an XML file to save the customers. So I create an XML file (if not existing) and fill it with elements. Here you can see how I do:

string path = "C:\\Users\\Römel\\Desktop\\Save";
List<Kunde> kunde = new List<Kunde>();

private void Form1_Load(object sender, EventArgs e)
{
    XmlDocument xdocument = new XmlDocument();
    xdocument.Load(path + "\\save.xml");
    foreach (XmlNode xnode in xdocument.SelectNodes("Kundenverwaltung/Kunde"))
    {
        Kunde k = new Kunde();
        k.KundenNr = xnode.SelectSingleNode("KundenNr").InnerText;
        k.Nachname = xnode.SelectSingleNode("Nachname").InnerText;
        k.Vorname = xnode.SelectSingleNode("Vorname").InnerText;
        k.Adresse = xnode.SelectSingleNode("Adresse").InnerText;
        k.Ort = xnode.SelectSingleNode("Ort").InnerText;
        k.Telefon = xnode.SelectSingleNode("Telefon").InnerText;
        k.Mail = xnode.SelectSingleNode("Mail").InnerText;

        kunde.Add(k);
        listViewKunden.Items.Add(k.Nachname + " " + k.Vorname);
    }
}

private void btnAddCustomer_Click(object sender, EventArgs e)
{
    if(!Directory.Exists(path))
    {
        Directory.CreateDirectory(path);
    }

    if (!File.Exists(path + "\\save.xml"))
    {
        XmlTextWriter xwriter = new XmlTextWriter(path + "\\save.xml", Encoding.UTF8);
        xwriter.WriteStartElement("Kundenverwaltung");
        xwriter.WriteEndElement();
        xwriter.Close();
    }

    Kunde k = new Kunde();
    k.KundenNr = txtKundenNr.Text;
    k.Nachname = txtKundeNachname.Text;
    k.Vorname = txtKundeVorname.Text;
    k.Adresse = txtKundeAdresse.Text;
    k.Ort = txtKundeOrt.Text;
    k.Telefon = txtKundeTel.Text;
    k.Mail = txtKundeMail.Text;

    kunde.Add(k);
    listViewKunden.Items.Add(k.Nachname + " " + k.Vorname);

    txtKundenNr.Text = "";
    txtKundeNachname.Text = "";
    txtKundeVorname.Text = "";
    txtKundeAdresse.Text = "";
    txtKundeOrt.Text = "";
    txtKundeTel.Text = "";
    txtKundeMail.Text = "";
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    XmlDocument xdoc = new XmlDocument();
    xdoc.Load(path + "\\save.xml");
    XmlNode xnode = xdoc.SelectSingleNode("Kundenverwaltung");
    xnode.RemoveAll();

    foreach (Kunde k in kunde)
    {
        XmlNode xtop = xdoc.CreateElement("Kunde");
        XmlNode xkundennr = xdoc.CreateElement("KundenNr");
        XmlNode xnachname = xdoc.CreateElement("Nachname");
        XmlNode xvorname = xdoc.CreateElement("Vorname");
        XmlNode xadresse = xdoc.CreateElement("Adresse");
        XmlNode xort = xdoc.CreateElement("Ort");
        XmlNode xtel = xdoc.CreateElement("Telefon");
        XmlNode xmail = xdoc.CreateElement("Mail");

        xkundennr.InnerText = k.KundenNr;
        xnachname.InnerText = k.Vorname;
        xvorname.InnerText = k.Nachname;
        xadresse.InnerText = k.Adresse;
        xort.InnerText = k.Ort;
        xtel.InnerText = k.Telefon;
        xmail.InnerText = k.Mail;

        xtop.AppendChild(xkundennr);
        xtop.AppendChild(xnachname);
        xtop.AppendChild(xvorname);
        xtop.AppendChild(xadresse);
        xtop.AppendChild(xort);
        xtop.AppendChild(xtel);
        xtop.AppendChild(xmail);

        xdoc.DocumentElement.AppendChild(xtop);
    }

    xdoc.Save(path + "\\save.xml");

    e.Cancel = false;
    this.Close();
}

For KundenNr , Nachname etc. I created some classes in the end of my file. I hope it's not too much code.

First: I can't use the regular "X" button in upper right corner because there is no reaction if I hit it. So I created an Exit button.

Second: Everytime I hit the Exit button for leaving the program there comes an error at this line:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    xdoc.Load(path + "\\save.xml");
}

"The root element is missing" comes up. Does someone know this error and can give me a hint?

EDIT: For btnExit_Click I use this code:

private void btnExit_Click(object sender, EventArgs e)
{
    this.Close();
}

XmlDocument is enforcing the rule that a valid XML document must have one root element.

Not sure exactly what your code is doing, but I suspect it is creating XML which might look like:

<Kunde ... />
<Kunde ... />
<Kunde ... />
<Kunde ... />
<Kunde ... />

... where it needs to look something like:

<Kunden>
    <Kunde ... />
    <Kunde ... />
    <Kunde ... />
    <Kunde ... />
    <Kunde ... />
</Kunden>

In other words, you need to create one and only one element to add directly to the document (which becomes your root element) and add all other elements inside that.

You can Load XmlDocument from one thing and save it to another. It's an in memory representation. Given you are doing a total overwrite trying to deal with that is unnecessary, it's way more work than creating a root node.

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
  XmlDocument xdoc = new XmlDocument();
  xdoc.LoadXml("<Kundenverwaltung"></Kundenverwaltung>"); // You might want to put any xml directives, default attributes, etc. in here.
  XmlNode xnode = xdoc.SelectSingleNode("Kundenverwaltung");
  foreach (Kunde k in kunde)
  {
     //Create and add each Kunde node as before
  }
  xdoc.Save(path + "\\save.xml");
  e.Cancel = false;
}

So now all you do is delete the duff one you have now. I see some potential problems coming at you though. The file is being written whether there are any changes or not. XmlDocument holds all the content in memory, so this solution is only for smallish documents. If as is the case here something (suspect a bug at some point), your file gets stuffed up, all the data is lost. You might want to consider something like the above, but save instead to a temporary document. Then attempt to load or validate it, and if it comes back okay, then rename it to save, otherwise report the issue. That way at least only this "sessions" changes have been lost.

In general I never call XmlDoument.Load("Fred.xml") or save. I do something like.

XmlDocument doc = new XmlDocument();
using(FileStream rfs = new FileStream("OldFred.xml",FileMode.Open,FileAccess.Read))
{
   doc.Load(rfs);
   //do my reading stuff)
}
using(FileStream wfs = new FileStream("NewFred.xml",FileMode.Create,FileAccess.Write))
{
   //do my Writing stuff
   doc.Save(wfs);
}

The advantage of using FileStream is you'd get an immediate fail if Oldfred didn't exist, or you didn't have write access to the folder Where NewFred is for instance. It's a habit we picked up when dealing with concurrent and multi-user access to the file system. It means out XmlDocument reading and writing code never has to deal with the file system, the code that deals with the FileStreams takes care of that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM