I'm having trouble saving xml files after making changes to them. I've spent all day today trying to figure this out and I've gotten nowhere.
I have this xml doc:
<?xml version=1.0" encoding="utf-8"?>
<content>
<weapon id="1234" name="blahblah">
<note info="blah blah" />
</weapon>
<weapon id="5678" name="blahblah2">
<note info="blah blah" />
</weapon>
</content>
This is what I've come up with so far that doesn't exactly work (Edited to show how I read file):
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".xml");
StorageFile gfile = await openPicker.PickSingleFileAsync()
fileContent = await FileIO.ReadTextAsync(gfile, Windows.Storage.Streams.UnicodeEncoding.Utf8);
Xdocument xml = XDocument.Parse(fileContent);
xml.Descendants("weapon").Where(c => c.Attribute("id").Value.Equals("5678")).FirstorDefault().Remove();
IRandomAccessStream writeStream = await gfile.OpenAsync(FileAccessMode.ReadWrite);
Stream stream = writeStream.AsStreamForWrite();
xml.Save(stream);
The resulting xml doc would be something like this:
<?xml version=1.0" encoding="utf-8"?>
<content>
<weapon id="1234" name="blahblah">
<note info="blah blah" />
</content>apon>
<weapon id="5678" name="blahblah2">
<note info="blah blah" />
</weapon>
</content>
If I try to use FileAccessMode.ReadWriteNoCopyOnWrite for OpenAsync the file ends up being 0 bytes.
Anybody know how I can write this file correctly while still using XDocument.Save?
It turns out that this problem is more complex than it may seem at first sight.
The problems we need to solve include
After a lot of experimentation, the solution that I chose is to write the XML to a System.IO.MemoryStream, and then copy that memory stream to the storagefile. I do understand that this requires memory for a temporary copy of the data. But it works, is fast (buffered IO, small number of native Windows calls, only three awaits), trims correctly, and awaiting the operation actually works correctly. Note that this was not the case with some other methods I tried. Here's the solution:
MemoryStream ms = new MemoryStream()
xml.Save(ms, SaveOptions.DisableFormatting);
await ms.CopyToAsync(gfile);
The CopyToAsync extention method:
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage;
using Windows.Storage.Streams;
internal static class Extensions
{
public static async Task CopyToAsync(this MemoryStream source, StorageFile file)
{
using (IRandomAccessStream raStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
using (IOutputStream stream = raStream.GetOutputStreamAt(0))
{
await stream.WriteAsync(source.GetWindowsRuntimeBuffer());
await stream.FlushAsync();
}
raStream.Size = raStream.Position;
}
}
}