我想将大型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
,而无需创建大型临时字符串?
答案 0 :(得分:5)
XDocument.LoadAsync()
在.NET Core 2.0中可用:https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq.xdocument.loadasync?view=netcore-2.0
答案 1 :(得分:3)
首先,该任务不是异步运行的。您需要使用内置的异步IO命令或自己在线程池上启动任务。例如
.
|-- pom.xml
`-- src
|-- it
| |-- resources
| | `-- data.csv
| `-- scala #### TEST SOURCES ###
| `-- com
| `-- my
| `-- example
| `-- jetty
| `-- JettyBasedServerIT.scala
|-- main
| `-- scala #### SOURCES ###
| `-- com
| `-- my
| `-- example
| `-- jetty
| `-- JettyBasedServer.scala
`-- test
`-- scala #### TEST SOURCES ###
`-- com
`-- my
`-- example
`-- jetty
`-- MainArgumentsTest.scala
如果您使用Parse的stream version,那么您就不会获得临时字符串。
答案 2 :(得分:1)
迟到的答案,但我也需要在“传统”.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;
}
}