I have a WPF app which saves a few textboxes in a XML file.
But for some reason sometimes it saves ok and the other time it does not.

And the problem always is how it closes the XML file.
For example, closing the XML file shoudl end with

</Data>

But usualy when it goes wrong it has something like
</Data>Data> or even more.

Am i forced to clear my XML file each time before writing new data to it?

Code currently used for writing to it is.

    private void WriteXml(string filename)
    {
        tx.Name = txtName.Text;
        tx.HotKey = txtHotKey.Text;
        tx.Timer = txtDuration.Text;
        tx.Message = txtReminderMsg.Text;

        XmlSerializer writer = new XmlSerializer(typeof(Data));
        using (FileStream file = File.OpenWrite(filename))
        {
            writer.Serialize(file, tx);
        }
    }

Honeslty i have never used XML before in a project.
But by the looks of it its almost as if when one of the textboxes has less characters then before.
It will be saved wrong.

I assumed a XML file is saved line per line, not 1 big line with x amount of characters.

Dani AI

Generated

this is not an XML or XmlSerializer problem. It is the file mode. File.OpenWrite starts writing at position 0 but does not truncate the file, so any leftover bytes from the previous, longer save remain at the end. That is exactly why you sometimes see </Data>Data> trailing your document. See the official note under OpenWrite remarks. OpenWrite docs.

Instead of deleting the file first (your workaround with ), open the stream in a mode that overwrites or truncates in one step:

private void WriteXml(string filename)
{
    var serializer = new XmlSerializer(typeof(Data));
    using (var fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
    {
        serializer.Serialize(fs, tx);   // writes fresh content
        fs.Flush(true);                 // ensure it hits disk
    }
}

FileMode.Create creates the file if needed, or truncates it to zero length before writing if it already exists. FileMode.Create.

Two extra, robust options:

  • If you must keep OpenWrite for some reason, truncate after serializing: fs.SetLength(fs.Position); (requires a seekable, writable stream). SetLength docs.
  • For crash-safe updates, write to a temp file and swap it in:
    1. Serialize to tempPath with FileMode.Create.
    2. If filename exists, call File.Replace(tempPath, filename, filename + ".bak"); otherwise File.Move(tempPath, filename). File.Replace.

These tweaks remove the trailing-bytes issue and make the save path reliable.

Recommended Answers

All 5 Replies

Can't seem to replicate your issue using the following test code:

    public class Temp
    {
        public string Name;
        public string HotKey;
        public string Timer;
        public string Message;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Temp TempClass = new Temp();
            string FilePath = @"D:\Users\MASKEW\Desktop\Test.xml";

            TempClass.Name = "Name";
            TempClass.HotKey = "HotKey";
            TempClass.Timer = "Timer";
            TempClass.Message = "Message";

            XmlSerializer writer = new XmlSerializer(typeof(Temp));
            using (FileStream file = File.OpenWrite(FilePath))
            {
                writer.Serialize(file, TempClass);
            }
        }
    }

Any changes to the above to improve its accuracy?

Well that is the code i use to write to the XML.

From what i can tell, each time i change the text from a textbox to a shorter text.
I encounter the problem of having something after the closing of the XML </data>

Though when clearing the textboxes i use this code.

 private void btnClearSkill1_Click(object sender, RoutedEventArgs e)
        {
            txtName.Text = "NULL";
            txtDuration.Text = "0";
            txtHotKey.Text = "NULL";
            txtReminderMsg.Text = "NULL";
            if (rdbSkill1.IsChecked == true)
            {
                WriteXml("rdbSkill1.xml");
            }
            rdbSkill1.Content = ReadXml2("rdbSkill1.xml", 0);
        }

But i don't see what that being the problem.

You're right!

I also now see this behaviour if I shorten the text contained in one of the variables.

The solution I used was the following (line 21):

    public class Temp
    {
        public string Name;
        public string HotKey;
        public string Timer;
        public string Message;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Temp TempClass = new Temp();
            string FilePath = @"D:\Users\MASKEW\Desktop\Test.xml";

            TempClass.Name = "Na";
            TempClass.HotKey = "Hot";
            TempClass.Timer = "Timer";
            TempClass.Message = "Message";

            File.Delete(FilePath);
            XmlSerializer writer = new XmlSerializer(typeof(Temp));
            using (FileStream file = File.OpenWrite(FilePath))
            {
                writer.Serialize(file, TempClass);
            }
        }
    }

This deletes the file and then re-creates it meaning there is no data corruption from the previous entries.

Great that did it, thanks a lot.

Find it odd XML's are saved like this, but at least i got a solution.

Yeah I think it is something to do with how the serialize works. Not sure though.

Glad it's sorted.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.