2012-05-24 50 views
3

我無法在對它們進行更改後保存xml文件。我今天花了整整一天的時間試圖弄清楚這一點,而且我無處可去。XML,LINQ和XDocument.Save問題

我有這樣的XML文檔:

<?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.ReadWriteNoCopyOnWrit e爲OpenAsync文件結束爲0字節。

任何人都知道如何在仍使用XDocument.Save的情況下正確編寫此文件?

+0

您是否在Save()調用之後關閉或處置流? – Steve

+0

掛上,不完整,需要測試。 –

+0

我遺漏了Dispose,但它的行爲與此相同。 – AveryPierce

回答

1

事實證明,這個問題比看起來更復雜。

我們需要解決的問題包括

  • 寫入文件異步
  • 寫有效,即用緩衝IO
  • 覆蓋整個文件,如果需要的話
  • 化妝修整現有的文件我們的等待寫入操作

經過大量實驗後,選擇的是將XML寫入System.IO.MemoryStream,然後將該內存流複製到存儲文件。我明白,這需要內存來存儲數據的臨時副本。但它工作,速度很快(IO緩衝,少量本地Windows調用,只有三個等待),修剪正確,並等待操作實際正常工作。請注意,我嘗試的其他一些方法並非如此。這裏的解決方案:

MemoryStream ms = new MemoryStream() 

xml.Save(ms, SaveOptions.DisableFormatting); 

await ms.CopyToAsync(gfile); 

的CopyToAsync extention方法:

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

發生同樣的事情。只有文件的開頭被替換。只要文件獲得更多內容,這種方式就可以工作,但如果刪除內容,舊內容仍然會被留下。我被告知StorageFile.ReplaceWithStreamedFileAsync會訣竅,但我無法弄清楚如何使用它。 – AveryPierce

+0

「如果刪除舊內容仍然存在的內容」 - >確實,我更新瞭解決該問題的答案。 –

+0

謝謝。這樣一個簡單的任務不應該那麼困難。 – AveryPierce

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。

+0

感謝您的回覆。我不認爲我可以使用CreateFileAsync,因爲它需要一個我目前沒有或不知道如何獲得的StorageFolder。我使用FileOpenPicker或AccessListEntryView打開文件,我擁有的是StorageFile。我正在使用FileIO.ReadTextAsync()來讀取文件。 – AveryPierce

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的硬編碼列表,我們創建一個基於列表的XDocument。最後我們要求用戶提供一個可寫的流來保存我們的XDocument。(我們也可以從獨立存儲獲得可寫入的流)。最後,我們只需使用適當的Stream來調用寫入方法。

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

我正在寫入一個FileStream,它將刷新到磁盤。

public static async Task SaveXMLAsync(XDocument linqXml, StorageFolder localFolder, string filename) 
{ 
    // Create a storage file 
    StorageFile file = await localFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting); 

    // Write the XML to a File Stream. 
    using (FileStream sourceStream = new FileStream(file.Path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) 
    { 
    linqXml.Save(sourceStream); 

    // Async flush to disk. 
    await sourceStream.FlushAsync(); 
    }; 
}