簡體   English   中英

異步加載 XDocument

[英]Load XDocument asynchronously

我想將大型 XML 文檔加載到 XDocument 對象中。 使用XDocument.Load(path, loadOptions)的簡單同步方法效果很好,但在加載大文件(尤其是來自網絡存儲)時,在 GUI 上下文中會阻塞很長時間。

我編寫這個異步版本的目的是提高文檔加載的響應能力,特別是在通過網絡加載文件時。

    public static async Task<XDocument> LoadAsync(String path, LoadOptions loadOptions = LoadOptions.PreserveWhitespace)
    {
        String xml;

        using (var stream = File.OpenText(path))
        {
            xml = await stream.ReadToEndAsync();
        }

        return XDocument.Parse(xml, loadOptions);
    }

但是,在從本地磁盤加載的 200 MB XML 原始文件上,同步版本會在幾秒鍾內完成。 異步版本(在 32 位上下文中運行)反而拋出OutOfMemoryException

   at System.Text.StringBuilder.ToString()
   at System.IO.StreamReader.<ReadToEndAsyncInternal>d__62.MoveNext()

我想這是因為臨時字符串變量用於將原始 XML 保存在內存中以供XDocument解析。 大概在同步場景中, XDocument.Load()能夠通過源文件流式傳輸,並且永遠不需要創建單個巨大的 String 來保存整個文件。

有什么辦法可以兩全其美嗎? 使用完全異步 I/O 加載XDocument ,而無需創建大型臨時字符串?

XDocument.LoadAsync()在 .NET Core 2.0 中可用: https : XDocument.LoadAsync() 2.0

遲到的答案,但我也需要在“舊版”.NET Framework 版本上進行異步讀取,所以我想出了一種方法來真正以異步方式讀取內容,而無需恢復到在內存中緩沖 XML 數據。

由於XDocument.CreateWriter()提供的 writer 不支持異步寫入,因此XmlWriter.WriteNodeAsync()失敗,代碼執行異步讀取並將其轉換為 XDocument-writer 上的同步寫入。 然而,代碼的靈感來自XmlWriter.WriteNodeAsync()工作方式。 由於作者構建了一個內存中的 DOM,這實際上比實際進行異步寫入更好。

public static async Task<XDocument> LoadAsync(Stream stream, LoadOptions loadOptions) {
    using (var reader = XmlReader.Create(stream, new XmlReaderSettings() {
            DtdProcessing = DtdProcessing.Ignore,
            IgnoreWhitespace = (loadOptions&LoadOptions.PreserveWhitespace) == LoadOptions.None,
            XmlResolver = null,
            CloseInput = false,
            Async = true
    })) {
        var result = new XDocument();
        using (var writer = result.CreateWriter()) {
            do {
                switch (reader.NodeType) {
                case XmlNodeType.Element:
                    writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
                    writer.WriteAttributes(reader, true);
                    if (reader.IsEmptyElement) {
                        writer.WriteEndElement();
                    }
                    break;
                case XmlNodeType.Text:
                    writer.WriteString(await reader.GetValueAsync().ConfigureAwait(false));
                    break;
                case XmlNodeType.CDATA:
                    writer.WriteCData(reader.Value);
                    break;
                case XmlNodeType.EntityReference:
                    writer.WriteEntityRef(reader.Name);
                    break;
                case XmlNodeType.ProcessingInstruction:
                case XmlNodeType.XmlDeclaration:
                    writer.WriteProcessingInstruction(reader.Name, reader.Value);
                    break;
                case XmlNodeType.Comment:
                    writer.WriteComment(reader.Value);
                    break;
                case XmlNodeType.DocumentType:
                    writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);
                    break;
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace:
                    writer.WriteWhitespace(await reader.GetValueAsync().ConfigureAwait(false));
                    break;
                case XmlNodeType.EndElement:
                    writer.WriteFullEndElement();
                    break;
                }
            } while (await reader.ReadAsync().ConfigureAwait(false));
        }
        return result;
    }
}

首先,任務不是異步運行的。 您需要使用內置的異步 IO 命令或自己在線程池上啟動任務。 例如

public static Task<XDocument> LoadAsync
 ( String path
 , LoadOptions loadOptions = LoadOptions.PreserveWhitespace
 )
{
    return Task.Run(()=>{
     using (var stream = File.OpenText(path))
        {
            return XDocument.Load(stream, loadOptions);
        }
    });
}

如果您使用 Parse 的流版本,則不會獲得臨時字符串。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM