XML,LINQ和XDocument.Save问题

时间:2012-05-24 23:45:06

标签: c# xml windows-8 windows-runtime

我在更改xml文件后遇到问题。我今天花了一整天的时间试图解决这个问题而且我无处可去。

我有这个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>

这是我到目前为止所提出的并不完全正常工作(编辑以显示我如何阅读文件):

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);

生成的xml文档将是这样的:

<?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>

如果我尝试将FileAccessMode.ReadWriteNoCopyOnWrite用于OpenAsync,则文件最终为0字节。

有人知道如何在使用XDocument.Save时正确编写此文件吗?

5 个答案:

答案 0 :(得分:2)

为什么不直接使用System.IO.File.WriteAllText?

XDocument xml = XDocument.Load(xmlFilePath);

System.IO.File.WriteAllText(xmlFilePath, string.Format(@"<?xml version=""1.0""?>{0}{1}", Environment.NewLine, xml));

答案 1 :(得分:1)

事实证明,这个问题比初看起来更复杂。

我们需要解决的问题包括

  • 异步写入文件
  • 高效写入,即使用缓冲IO
  • 覆盖整个文件,必要时修剪现有文件
  • 让我们的写作操作等待

经过大量实验,我选择的解决方案是将XML写入System.IO.MemoryStream,然后将该内存流复制到存储文件中。我知道这需要内存来存储数据的临时副本。但它工作正常,快速(缓冲IO,少量本机Windows调用,只有三个等待),正确修剪,等待操作实际上正常工作。请注意,我尝试过的其他一些方法并非如此。这是解决方案:

MemoryStream ms = new MemoryStream()

xml.Save(ms, SaveOptions.DisableFormatting);

await ms.CopyToAsync(gfile);

CopyToAsync扩展方法:

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;
        }
    }
}

答案 2 :(得分:0)

我得到了它的工作,但我需要对你的代码行为做更多的研究。试试这个......

var local = Windows.Storage.ApplicationData.Current.LocalFolder;
var file = await local.GetFileAsync("test.xml");
var data = await FileIO.ReadTextAsync(file);
var xml = XDocument.Parse(data);

xml.Descendants("weapon").Where(c => c.Attribute("id").Value.Equals("5678")).FirstOrDefault().Remove();

file = await local.CreateFileAsync("test.xml", CreationCollisionOption.ReplaceExisting);
var writeStream = await file.OpenStreamForWriteAsync() as Stream;

xml.Save(writeStream);

writeStream.Flush();

其中test.xml是本地文件夹中包含原始XML的文件。

答案 3 :(得分:0)

嗨,今天我必须编写一个XML文件,如果我提供一个有效的流,如果我XDocument.Save运行良好,它基本上运行良好。

WinRT可能很棘手,因为它对文件系统的访问权限有限。

/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{

    public List<DataStructure> DataList { get; set; }

    public MainPage()
    {
        this.InitializeComponent();
        DataList = Enumerable.Range(0, 25).Select(i => new DataStructure() { Id = i, Data = string.Format("Number : {0}", i) }).ToList();
        this.Loaded += MainPage_Loaded;
    }

    async void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        this.Loaded -= MainPage_Loaded;
        //var xmlDocument =
        //    new XDocument(
        //        new XElement("DataList",
        //            DataList.Select(dataItem =>
        //                new XElement("DataItem",
        //                    new XAttribute("id", dataItem.Id), new XAttribute("data", dataItem.Data)))));

        var rootNode = new XElement("DataList");
        var xmlDocument = new XDocument(rootNode);
        foreach (var dataItem in DataList)
        {
            rootNode.Add(new XElement("DataItem",
                            new XAttribute("id", dataItem.Id), new XAttribute("data", dataItem.Data)));
        }

        FileSavePicker savePicker = new FileSavePicker();
        savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
        // Dropdown of file types the user can save the file as
        savePicker.FileTypeChoices.Add("XML Document", new List<string>() { ".xml" });
        // Default file name if the user does not type one in or select a file to replace
        savePicker.SuggestedFileName = "New Xml Document";

        StorageFile file = await savePicker.PickSaveFileAsync();
        // Process picked file
        if (file != null)
        {
            // Store file for future access
            var fileToken = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.Add(file);
            var writterStream = await file.OpenStreamForWriteAsync();
            xmlDocument.Save(writterStream);
        }

    }

我在VS2012(RC)上创建了一个WinRT默认项目。然后我修改了MainPage.cs文件,使其看起来像上一个。该文件包含一个硬编码的DataStructures列表,我们根据List创建一个XDocument。最后,我们要求用户提供可写流以保存我们的XDocument。 (我们还可以从隔离存储中获取可写流)。最后,我们只使用正确的Stream调用write方法。

答案 4 :(得分:0)

我正在写一个FileStream,它会刷新到磁盘。

{{1}}