Search code examples
c#xmllinqxelement

xml XDocument Save modify file


I am trying to add XElements to an XDocument. I am trying to move from using inifile to xml but I am having some issues here.

This is my C# code:

public static async Task<List<XElement>> XMLHandling()
        {
            StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
            StorageFile file = await storageFolder.GetFileAsync("sFile.xml");

            List<XElement> doc;
            using (var fileStream = await file.OpenStreamForReadAsync())
            {
                doc = XDocument.Load(fileStream).Descendants("signals").ToList();
                //avoid range check
                doc[0].Add(new XElement("Character", new XElement("Value", "value1"), new XElement("Description", "This is some text")));
                using (Stream sr = await file.OpenStreamForWriteAsync())
                {
                    doc[0].Save(sr);
                }
            }
            //example
            return doc;
        }

This is how the file look before:

<?xml version="1.0"?>
<catalog>
   <signals>

   </signals>

   <book id="bk101">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications 
      with XML.</description>
   </book>
</catalog>

and this is how it becomes after:

<?xml version="1.0" encoding="utf-8"?>
<signals>
  <Character>
    <Value>value1</Value>
    <Description>This is some text</Description>
  </Character>
</signals>/title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications 
      with XML.</description>
   </book>
</catalog>

I can't even understand why and I can't debug this. Any ideas?


Solution

  • There are at least two things wrong here.

    First, you're not saving what you thing you are.

    doc = XDocument.Load(fileStream).Descendants("signals").ToList();
    

    Here, doc is list of signals elements.

    doc[0].Add(new XElement("Character", new XElement("Value", "value1"), 
        new XElement("Description", "This is some text")));
    

    Here, you add a Character element to the first signals element.

    doc[0].Save(sr);
    

    And finally, you save only the first signals element. That explains why you see a signals at the start of your 'new' document.

    Secondly, you're not truncating the file you're writing to. This is why you see some garbled XML after the end of your signals element - this is simply what was in the file originally, you've just overwritten the first part of it. You might want to see this question for some options to handle this. One option is just to set the length to zero before you write to it.

    So, something like this:

    XDocument doc;
    
    using (var stream = await file.OpenStreamForReadAsync())
    {
        doc = XDocument.Load(stream);
    }
    
    var signals = doc.Descendants("signals").Single();
    
    signals.Add(new XElement("Character", ...));
    
    using (var stream = await file.OpenStreamForWriteAsync())
    {
        stream.SetLength(0);
        doc.Save(stream);
    }