From d88ba5b82dd0d1164869fe6d5fbcd41c7a62f26c Mon Sep 17 00:00:00 2001 From: sepidehMS Date: Thu, 29 Dec 2016 15:59:43 -0800 Subject: [PATCH] Add async XLinq document/element loading and saving Commit migrated from https://github.com/dotnet/corefx/commit/318ede2acc1bb1ecb75d1c8b0bada2135081d9e6 --- .../src/System/Xml/Linq/XCData.cs | 24 ++ .../src/System/Xml/Linq/XComment.cs | 19 ++ .../src/System/Xml/Linq/XContainer.cs | 288 ++++++++++++------ .../src/System/Xml/Linq/XDocument.cs | 244 ++++++++++++++- .../src/System/Xml/Linq/XDocumentType.cs | 21 ++ .../src/System/Xml/Linq/XElement.cs | 259 +++++++++++++++- .../src/System/Xml/Linq/XLinq.cs | 80 +++++ .../src/System/Xml/Linq/XNode.cs | 75 +++++ .../System/Xml/Linq/XProcessingInstruction.cs | 19 ++ .../src/System/Xml/Linq/XText.cs | 23 ++ .../tests/XDocument.Common/BridgeHelpers.cs | 2 +- .../HelperExtensionMethods.cs | 2 - .../XDocument.Common/XDocument.Common.csproj | 3 - .../XDocument.Test.ModuleCore.csproj | 3 - .../tests/XDocument.Test.ModuleCore/util.cs | 2 - .../tests/misc/LoadSaveAsyncTests.cs | 206 +++++++++++++ .../misc/System.Xml.Linq.Misc.Tests.csproj | 5 +- .../ref/System.Xml.XDocument.cs | 47 +++ .../ref/System.Xml.XDocument.csproj | 1 + .../src/ApiCompatBaseline.net463.txt | 23 ++ 20 files changed, 1227 insertions(+), 119 deletions(-) create mode 100644 src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs create mode 100644 src/libraries/System.Xml.XDocument/src/ApiCompatBaseline.net463.txt diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XCData.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XCData.cs index 551da96934e71..e9eee51faa7c6 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XCData.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XCData.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Threading; +using System.Threading.Tasks; + namespace System.Xml.Linq { /// @@ -49,6 +52,27 @@ public override void WriteTo(XmlWriter writer) writer.WriteCData(text); } + /// + /// Write this to the given . + /// + /// + /// The to write this to. + /// + /// + /// The CancellationToken to use to request cancellation of this operation. + /// + /// + /// A Task that represents the eventual completion of the operation. + /// + public override Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return writer.WriteCDataAsync(text); + } + internal override XNode CloneNode() { return new XCData(this); diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XComment.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XComment.cs index 486d30e1bccb7..9018092dd4275 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XComment.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XComment.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Threading; +using System.Threading.Tasks; + namespace System.Xml.Linq { /// @@ -93,6 +96,22 @@ public override void WriteTo(XmlWriter writer) writer.WriteComment(value); } + /// + /// Write this to the passed in . + /// + /// + /// The to write this to. + /// + /// A cancellation token. + public override Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return writer.WriteCommentAsync(value); + } + internal override XNode CloneNode() { return new XComment(this); diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XContainer.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XContainer.cs index 1315570edc5a7..4c10ff7e3b787 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XContainer.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XContainer.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Debug = System.Diagnostics.Debug; using IEnumerable = System.Collections.IEnumerable; @@ -853,53 +855,117 @@ internal static string GetStringValue(object value) internal void ReadContentFrom(XmlReader r) { if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); - XContainer c = this; - NamespaceCache eCache = new NamespaceCache(); - NamespaceCache aCache = new NamespaceCache(); + + ContentReader cr = new ContentReader(this); + while (cr.ReadContentFrom(this, r) && r.Read()) ; + } + + internal void ReadContentFrom(XmlReader r, LoadOptions o) + { + if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == 0) + { + ReadContentFrom(r); + return; + } + if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + ContentReader cr = new ContentReader(this); + while (cr.ReadContentFrom(this, r, o) && r.Read()) ; + } + + internal async Task ReadContentFromAsync(XmlReader r, CancellationToken cancellationToken) + { + if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + ContentReader cr = new ContentReader(this); do + { + cancellationToken.ThrowIfCancellationRequested(); + } + while (cr.ReadContentFrom(this, r) && await r.ReadAsync().ConfigureAwait(false)); + } + + internal async Task ReadContentFromAsync(XmlReader r, LoadOptions o, CancellationToken cancellationToken) + { + if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == 0) + { + await ReadContentFromAsync(r, cancellationToken).ConfigureAwait(false); + return; + } + if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + ContentReader cr = new ContentReader(this); + do + { + cancellationToken.ThrowIfCancellationRequested(); + } + while (cr.ReadContentFrom(this, r, o) && await r.ReadAsync().ConfigureAwait(false)); + } + + private sealed class ContentReader + { + private readonly NamespaceCache _eCache = new NamespaceCache(); + private readonly NamespaceCache _aCache = new NamespaceCache(); + private readonly IXmlLineInfo _lineInfo; + private XContainer _currentContainer; + private string _baseUri; + + public ContentReader(XContainer rootContainer) + { + _currentContainer = rootContainer; + } + + public ContentReader(XContainer rootContainer, XmlReader r, LoadOptions o) + { + _currentContainer = rootContainer; + _baseUri = (o & LoadOptions.SetBaseUri) != 0 ? r.BaseURI : null; + _lineInfo = (o & LoadOptions.SetLineInfo) != 0 ? r as IXmlLineInfo : null; + } + + public bool ReadContentFrom(XContainer rootContainer, XmlReader r) { switch (r.NodeType) { case XmlNodeType.Element: - XElement e = new XElement(eCache.Get(r.NamespaceURI).GetName(r.LocalName)); + XElement e = new XElement(_eCache.Get(r.NamespaceURI).GetName(r.LocalName)); if (r.MoveToFirstAttribute()) { do { - e.AppendAttributeSkipNotify(new XAttribute(aCache.Get(r.Prefix.Length == 0 ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value)); + e.AppendAttributeSkipNotify(new XAttribute(_aCache.Get(r.Prefix.Length == 0 ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value)); } while (r.MoveToNextAttribute()); r.MoveToElement(); } - c.AddNodeSkipNotify(e); + _currentContainer.AddNodeSkipNotify(e); if (!r.IsEmptyElement) { - c = e; + _currentContainer = e; } break; case XmlNodeType.EndElement: - if (c.content == null) + if (_currentContainer.content == null) { - c.content = string.Empty; + _currentContainer.content = string.Empty; } - if (c == this) return; - c = c.parent; + if (_currentContainer == rootContainer) return false; + _currentContainer = _currentContainer.parent; break; case XmlNodeType.Text: case XmlNodeType.SignificantWhitespace: case XmlNodeType.Whitespace: - c.AddStringSkipNotify(r.Value); + _currentContainer.AddStringSkipNotify(r.Value); break; case XmlNodeType.CDATA: - c.AddNodeSkipNotify(new XCData(r.Value)); + _currentContainer.AddNodeSkipNotify(new XCData(r.Value)); break; case XmlNodeType.Comment: - c.AddNodeSkipNotify(new XComment(r.Value)); + _currentContainer.AddNodeSkipNotify(new XComment(r.Value)); break; case XmlNodeType.ProcessingInstruction: - c.AddNodeSkipNotify(new XProcessingInstruction(r.Name, r.Value)); + _currentContainer.AddNodeSkipNotify(new XProcessingInstruction(r.Name, r.Value)); break; case XmlNodeType.DocumentType: - c.AddNodeSkipNotify(new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value)); + _currentContainer.AddNodeSkipNotify(new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value)); break; case XmlNodeType.EntityReference: if (!r.CanResolveEntity) throw new InvalidOperationException(SR.InvalidOperation_UnresolvedEntityReference); @@ -910,109 +976,97 @@ internal void ReadContentFrom(XmlReader r) default: throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnexpectedNodeType, r.NodeType)); } - } while (r.Read()); - } - - internal void ReadContentFrom(XmlReader r, LoadOptions o) - { - if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == 0) - { - ReadContentFrom(r); - return; + return true; } - if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); - XContainer c = this; - XNode n = null; - NamespaceCache eCache = new NamespaceCache(); - NamespaceCache aCache = new NamespaceCache(); - string baseUri = (o & LoadOptions.SetBaseUri) != 0 ? r.BaseURI : null; - IXmlLineInfo li = (o & LoadOptions.SetLineInfo) != 0 ? r as IXmlLineInfo : null; - do + + public bool ReadContentFrom(XContainer rootContainer, XmlReader r, LoadOptions o) { - string uri = r.BaseURI; + XNode newNode = null; + string baseUri = r.BaseURI; + switch (r.NodeType) { case XmlNodeType.Element: + { + XElement e = new XElement(_eCache.Get(r.NamespaceURI).GetName(r.LocalName)); + if (_baseUri != null && _baseUri != baseUri) { - XElement e = new XElement(eCache.Get(r.NamespaceURI).GetName(r.LocalName)); - if (baseUri != null && baseUri != uri) - { - e.SetBaseUri(uri); - } - if (li != null && li.HasLineInfo()) - { - e.SetLineInfo(li.LineNumber, li.LinePosition); - } - if (r.MoveToFirstAttribute()) - { - do - { - XAttribute a = new XAttribute(aCache.Get(r.Prefix.Length == 0 ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value); - if (li != null && li.HasLineInfo()) - { - a.SetLineInfo(li.LineNumber, li.LinePosition); - } - e.AppendAttributeSkipNotify(a); - } while (r.MoveToNextAttribute()); - r.MoveToElement(); - } - c.AddNodeSkipNotify(e); - if (!r.IsEmptyElement) + e.SetBaseUri(baseUri); + } + if (_lineInfo != null && _lineInfo.HasLineInfo()) + { + e.SetLineInfo(_lineInfo.LineNumber, _lineInfo.LinePosition); + } + if (r.MoveToFirstAttribute()) + { + do { - c = e; - if (baseUri != null) + XAttribute a = new XAttribute(_aCache.Get(r.Prefix.Length == 0 ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value); + if (_lineInfo != null && _lineInfo.HasLineInfo()) { - baseUri = uri; + a.SetLineInfo(_lineInfo.LineNumber, _lineInfo.LinePosition); } - } - break; + e.AppendAttributeSkipNotify(a); + } while (r.MoveToNextAttribute()); + r.MoveToElement(); } - case XmlNodeType.EndElement: + _currentContainer.AddNodeSkipNotify(e); + if (!r.IsEmptyElement) { - if (c.content == null) - { - c.content = string.Empty; - } - // Store the line info of the end element tag. - // Note that since we've got EndElement the current container must be an XElement - XElement e = c as XElement; - Debug.Assert(e != null, "EndElement received but the current container is not an element."); - if (e != null && li != null && li.HasLineInfo()) - { - e.SetEndElementLineInfo(li.LineNumber, li.LinePosition); - } - if (c == this) return; - if (baseUri != null && c.HasBaseUri) + _currentContainer = e; + if (_baseUri != null) { - baseUri = c.parent.BaseUri; + _baseUri = baseUri; } - c = c.parent; - break; } + break; + } + case XmlNodeType.EndElement: + { + if (_currentContainer.content == null) + { + _currentContainer.content = string.Empty; + } + // Store the line info of the end element tag. + // Note that since we've got EndElement the current container must be an XElement + XElement e = _currentContainer as XElement; + Debug.Assert(e != null, "EndElement received but the current container is not an element."); + if (e != null && _lineInfo != null && _lineInfo.HasLineInfo()) + { + e.SetEndElementLineInfo(_lineInfo.LineNumber, _lineInfo.LinePosition); + } + if (_currentContainer == rootContainer) return false; + if (_baseUri != null && _currentContainer.HasBaseUri) + { + _baseUri = _currentContainer.parent.BaseUri; + } + _currentContainer = _currentContainer.parent; + break; + } case XmlNodeType.Text: case XmlNodeType.SignificantWhitespace: case XmlNodeType.Whitespace: - if ((baseUri != null && baseUri != uri) || - (li != null && li.HasLineInfo())) + if ((_baseUri != null && _baseUri != baseUri) || + (_lineInfo != null && _lineInfo.HasLineInfo())) { - n = new XText(r.Value); + newNode = new XText(r.Value); } else { - c.AddStringSkipNotify(r.Value); + _currentContainer.AddStringSkipNotify(r.Value); } break; case XmlNodeType.CDATA: - n = new XCData(r.Value); + newNode = new XCData(r.Value); break; case XmlNodeType.Comment: - n = new XComment(r.Value); + newNode = new XComment(r.Value); break; case XmlNodeType.ProcessingInstruction: - n = new XProcessingInstruction(r.Name, r.Value); + newNode = new XProcessingInstruction(r.Name, r.Value); break; case XmlNodeType.DocumentType: - n = new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value); + newNode = new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value); break; case XmlNodeType.EntityReference: if (!r.CanResolveEntity) throw new InvalidOperationException(SR.InvalidOperation_UnresolvedEntityReference); @@ -1023,20 +1077,25 @@ internal void ReadContentFrom(XmlReader r, LoadOptions o) default: throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnexpectedNodeType, r.NodeType)); } - if (n != null) + + if (newNode != null) { - if (baseUri != null && baseUri != uri) + if (_baseUri != null && _baseUri != baseUri) { - n.SetBaseUri(uri); + newNode.SetBaseUri(baseUri); } - if (li != null && li.HasLineInfo()) + + if (_lineInfo != null && _lineInfo.HasLineInfo()) { - n.SetLineInfo(li.LineNumber, li.LinePosition); + newNode.SetLineInfo(_lineInfo.LineNumber, _lineInfo.LinePosition); } - c.AddNodeSkipNotify(n); - n = null; + + _currentContainer.AddNodeSkipNotify(newNode); + newNode = null; } - } while (r.Read()); + + return true; + } } internal void RemoveNode(XNode n) @@ -1113,6 +1172,41 @@ internal void WriteContentTo(XmlWriter writer) } } + internal async Task WriteContentToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (content != null) + { + string stringContent = content as string; + + if (stringContent != null) + { + cancellationToken.ThrowIfCancellationRequested(); + + Task tWrite; + + if (this is XDocument) + { + tWrite = writer.WriteWhitespaceAsync(stringContent); + } + else + { + tWrite = writer.WriteStringAsync(stringContent); + } + + await tWrite.ConfigureAwait(false); + } + else + { + XNode n = (XNode)content; + do + { + n = n.next; + await n.WriteToAsync(writer, cancellationToken).ConfigureAwait(false); + } while (n != content); + } + } + } + private static void AddContentToList(List list, object content) { IEnumerable e = content is string ? null : content as IEnumerable; diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs index a7f3ccd36f20d..1957ec2e14289 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocument.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.IO; +using System.Threading; +using System.Threading.Tasks; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; using Encoding = System.Text.Encoding; @@ -268,6 +270,42 @@ public static XDocument Load(Stream stream, LoadOptions options) } } + /// + /// Create a new and initialize its underlying XML tree using + /// the passed parameter. Optionally whitespace handling + /// can be preserved. + /// + /// + /// If LoadOptions.PreserveWhitespace is enabled then + /// the underlying property + /// is set to false. + /// + /// + /// A containing the raw XML to read into the newly + /// created . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// + /// A new containing the contents of the passed in + /// . + /// + public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken cancellationToken) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + + using (XmlReader r = XmlReader.Create(stream, rs)) + { + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); + } + } + /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. @@ -315,6 +353,42 @@ public static XDocument Load(TextReader textReader, LoadOptions options) } } + /// + /// Create a new and initialize its underlying XML tree using + /// the passed parameter. Optionally whitespace handling + /// can be preserved. + /// + /// + /// If LoadOptions.PreserveWhitespace is enabled then + /// the property + /// is set to false. + /// + /// + /// A containing the raw XML to read into the newly + /// created . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// + /// A new containing the contents of the passed in + /// . + /// + public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken cancellationToken) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + + using (XmlReader r = XmlReader.Create(textReader, rs)) + { + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); + } + } + /// /// Create a new containing the contents of the /// passed in . @@ -351,6 +425,62 @@ public static XDocument Load(XmlReader reader, LoadOptions options) { if (reader == null) throw new ArgumentNullException(nameof(reader)); if (reader.ReadState == ReadState.Initial) reader.Read(); + + XDocument d = InitLoad(reader, options); + d.ReadContentFrom(reader, options); + + if( !reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); + if (d.Root == null) throw new InvalidOperationException(SR.InvalidOperation_MissingRoot); + return d; + } + + /// + /// Create a new containing the contents of the + /// passed in . + /// + /// + /// An containing the XML to be read into the new + /// . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// + /// A new containing the contents of the passed + /// in . + /// + public static Task LoadAsync(XmlReader reader, LoadOptions options, CancellationToken cancellationToken) + { + if (reader == null) + throw new ArgumentNullException(nameof(reader)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return LoadAsyncInternal(reader, options, cancellationToken); + } + + private static async Task LoadAsyncInternal(XmlReader reader, LoadOptions options, CancellationToken cancellationToken) + { + if (reader.ReadState == ReadState.Initial) + { + await reader.ReadAsync().ConfigureAwait(false); + } + + XDocument d = InitLoad(reader, options); + await d.ReadContentFromAsync(reader, options, cancellationToken).ConfigureAwait(false); + + if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); + if (d.Root == null) throw new InvalidOperationException(SR.InvalidOperation_MissingRoot); + return d; + } + + /// + /// Performs shared initialization between Load and LoadAsync. + /// + static XDocument InitLoad(XmlReader reader, LoadOptions options) + { XDocument d = new XDocument(); if ((options & LoadOptions.SetBaseUri) != 0) { @@ -372,9 +502,6 @@ public static XDocument Load(XmlReader reader, LoadOptions options) { d.Declaration = new XDeclaration(reader); } - d.ReadContentFrom(reader, options); - if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); - if (d.Root == null) throw new InvalidOperationException(SR.InvalidOperation_MissingRoot); return d; } @@ -480,6 +607,40 @@ public void Save(Stream stream, SaveOptions options) } } + /// + /// Output this to a . + /// + /// + /// The to output the XML to. + /// + /// + /// If SaveOptions.DisableFormatting is enabled the output is not indented. + /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed. + /// + /// A cancellation token. + public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToken cancellationToken) + { + XmlWriterSettings ws = GetXmlWriterSettings(options); + + ws.Async = true; + + if (_declaration != null && !string.IsNullOrEmpty(_declaration.Encoding)) + { + try + { + ws.Encoding = Encoding.GetEncoding(_declaration.Encoding); + } + catch (ArgumentException) + { + } + } + + using (XmlWriter w = XmlWriter.Create(stream, ws)) + { + await WriteToAsync(w, cancellationToken).ConfigureAwait(false); + } + } + /// /// Output this to the passed in . /// @@ -529,6 +690,29 @@ public void Save(XmlWriter writer) WriteTo(writer); } + /// + /// Output this to a . + /// + /// + /// The to output the XML to. + /// + /// + /// If SaveOptions.DisableFormatting is enabled the output is not indented. + /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed. + /// + /// A cancellation token. + public async Task SaveAsync(TextWriter textWriter, SaveOptions options, CancellationToken cancellationToken) + { + XmlWriterSettings ws = GetXmlWriterSettings(options); + + ws.Async = true; + + using (XmlWriter w = XmlWriter.Create(textWriter, ws)) + { + await WriteToAsync(w, cancellationToken).ConfigureAwait(false); + } + } + /// /// Outputs this 's underlying XML tree. The output can /// be saved to a file, a , a , @@ -553,6 +737,20 @@ public void Save(string fileName) Save(fileName, GetSaveOptionsFromAnnotations()); } + /// + /// Output this to an . + /// + /// + /// The to output the XML to. + /// + /// + /// A cancellation token. + /// + public Task SaveAsync(XmlWriter writer, CancellationToken cancellationToken) + { + return WriteToAsync(writer, cancellationToken); + } + /// /// Output this to a file. /// @@ -611,6 +809,46 @@ public override void WriteTo(XmlWriter writer) writer.WriteEndDocument(); } + /// + /// Output this 's underlying XML tree to the + /// passed in . + /// + /// + /// + /// The to output the content of this + /// . + /// + /// A cancellation token. + public override Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return WriteToAsyncInternal(writer, cancellationToken); + } + + private async Task WriteToAsyncInternal(XmlWriter writer, CancellationToken cancellationToken) + { + Task tStart; + if (_declaration != null && _declaration.Standalone == "yes") + { + tStart = writer.WriteStartDocumentAsync(true); + } + else if (_declaration != null && _declaration.Standalone == "no") + { + tStart = writer.WriteStartDocumentAsync(false); + } + else + { + tStart = writer.WriteStartDocumentAsync(); + } + await tStart.ConfigureAwait(false); + + await WriteContentToAsync(writer, cancellationToken).ConfigureAwait(false); + await writer.WriteEndDocumentAsync().ConfigureAwait(false); + } + internal override void AddAttribute(XAttribute a) { throw new ArgumentException(SR.Argument_AddAttribute); diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocumentType.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocumentType.cs index 3f4a031d07d95..64c2ce977fa5c 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocumentType.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XDocumentType.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Threading; +using System.Threading.Tasks; + namespace System.Xml.Linq { /// @@ -143,6 +146,24 @@ public override void WriteTo(XmlWriter writer) writer.WriteDocType(_name, _publicId, _systemId, _internalSubset); } + /// + /// Write this to the passed in . + /// + /// + /// The to write this to. + /// + /// + /// A cancellation token. + /// + public override Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return writer.WriteDocTypeAsync(_name, _publicId, _systemId, _internalSubset); + } + internal override XNode CloneNode() { return new XDocumentType(this); diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs index 61ef13bbebbcf..c15850ea25d0e 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XElement.cs @@ -6,6 +6,8 @@ using System.IO; using System.Xml.Serialization; using System.Xml.Schema; +using System.Threading; +using System.Threading.Tasks; using CultureInfo = System.Globalization.CultureInfo; using IEnumerable = System.Collections.IEnumerable; @@ -137,11 +139,29 @@ internal XElement(XmlReader r) { } + private XElement(AsyncConstructionSentry s) + { + // Dummy ctor used to avoid public default ctor. This is used + // by async methods meant to perform the same operations as + // the XElement constructors that do synchronous processing; + // the async methods instead construct an XElement using this + // constructor (which doesn't do any processing) and then themselves + // do the async processing. This is because ctors can't be 'async'. + } + private struct AsyncConstructionSentry { } + internal XElement(XmlReader r, LoadOptions o) { ReadElementFrom(r, o); } + internal static async Task CreateAsync(XmlReader r, CancellationToken cancellationToken) + { + XElement xe = new XElement(default(AsyncConstructionSentry)); + await xe.ReadElementFromAsync(r, LoadOptions.None, cancellationToken).ConfigureAwait(false); + return xe; + } + /// /// Outputs this 's underlying XML tree. The output can /// be saved to a file, a , a , @@ -632,6 +652,42 @@ public static XElement Load(Stream stream, LoadOptions options) return Load(r, options); } } + + /// + /// Create a new and initialize its underlying XML tree using + /// the passed parameter. Optionally whitespace handling + /// can be preserved. + /// + /// + /// If LoadOptions.PreserveWhitespace is enabled then + /// the property + /// is set to false. + /// + /// + /// A containing the raw XML to read into the newly + /// created . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// A new containing the contents of the passed in + /// . + /// + public static async Task LoadAsync(Stream stream, LoadOptions options, CancellationToken cancellationToken) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + + using (XmlReader r = XmlReader.Create(stream, rs)) + { + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); + } + } + /// /// Create a new and initialize its underlying XML tree using /// the passed parameter. @@ -679,6 +735,41 @@ public static XElement Load(TextReader textReader, LoadOptions options) } } + /// + /// Create a new and initialize its underlying XML tree using + /// the passed parameter. Optionally whitespace handling + /// can be preserved. + /// + /// + /// If LoadOptions.PreserveWhitespace is enabled then + /// the property + /// is set to false. + /// + /// + /// A containing the raw XML to read into the newly + /// created . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// A new containing the contents of the passed in + /// . + /// + public static async Task LoadAsync(TextReader textReader, LoadOptions options, CancellationToken cancellationToken) + { + XmlReaderSettings rs = GetXmlReaderSettings(options); + + rs.Async = true; + + using (XmlReader r = XmlReader.Create(textReader, rs)) + { + return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false); + } + } + /// /// Create a new containing the contents of the /// passed in . @@ -721,6 +812,46 @@ public static XElement Load(XmlReader reader, LoadOptions options) return e; } + /// + /// Create a new containing the contents of the + /// passed in . + /// + /// + /// An containing the XML to be read into the new + /// . + /// + /// + /// A set of . + /// + /// + /// A cancellation token. + /// + /// A new containing the contents of the passed + /// in . + /// + public static Task LoadAsync(XmlReader reader, LoadOptions options, CancellationToken cancellationToken) + { + if (reader == null) + throw new ArgumentNullException(nameof(reader)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return LoadAsyncInternal(reader, options, cancellationToken); + } + + private static async Task LoadAsyncInternal(XmlReader reader, LoadOptions options, CancellationToken cancellationToken) + { + if (await reader.MoveToContentAsync().ConfigureAwait(false) != XmlNodeType.Element) throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ExpectedNodeType, XmlNodeType.Element, reader.NodeType)); + + XElement e = new XElement(new AsyncConstructionSentry()); + await e.ReadElementFromAsync(reader, options, cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + await reader.MoveToContentAsync().ConfigureAwait(false); + + if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile); + return e; + } + /// /// Parses a string containing XML into an . Optionally /// whitespace can be preserved. @@ -932,6 +1063,29 @@ public void Save(Stream stream, SaveOptions options) } } + /// + /// Output this to a . + /// + /// + /// The to output the XML to. + /// + /// + /// If SaveOptions.DisableFormatting is enabled the output is not indented. + /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed. + /// + /// A cancellation token. + public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToken cancellationToken) + { + XmlWriterSettings ws = GetXmlWriterSettings(options); + + ws.Async = true; + + using (XmlWriter w = XmlWriter.Create(stream, ws)) + { + await SaveAsync(w, cancellationToken).ConfigureAwait(false); + } + } + /// /// Output this to the passed in . /// @@ -970,6 +1124,29 @@ public void Save(TextWriter textWriter, SaveOptions options) } } + /// + /// Output this to a . + /// + /// + /// The to output the XML to. + /// + /// + /// If SaveOptions.DisableFormatting is enabled the output is not indented. + /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed. + /// + /// A cancellation token. + public async Task SaveAsync(TextWriter textWriter, SaveOptions options, CancellationToken cancellationToken) + { + XmlWriterSettings ws = GetXmlWriterSettings(options); + + ws.Async = true; + + using (XmlWriter w = XmlWriter.Create(textWriter, ws)) + { + await SaveAsync(w, cancellationToken).ConfigureAwait(false); + } + } + /// /// Output this to an . /// @@ -984,6 +1161,32 @@ public void Save(XmlWriter writer) writer.WriteEndDocument(); } + /// + /// Output this to an . + /// + /// + /// The to output the XML to. + /// + /// A cancellation token. + public Task SaveAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return SaveAsyncInternal(writer, cancellationToken); + } + + private async Task SaveAsyncInternal(XmlWriter writer, CancellationToken cancellationToken) + { + await writer.WriteStartDocumentAsync().ConfigureAwait(false); + + await WriteToAsync(writer, cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + await writer.WriteEndDocumentAsync().ConfigureAwait(false); + } + /// /// Sets the value of an attribute. The value is assigned to the attribute with the given /// name. If no attribute with the given name exists, a new attribute is added. If the @@ -1094,6 +1297,22 @@ public override void WriteTo(XmlWriter writer) new ElementWriter(writer).WriteElement(this); } + /// + /// Write this to the passed in . + /// + /// + /// The to write this to. + /// + /// A cancellation token. + public override Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return new ElementWriter(writer).WriteElementAsync(this, cancellationToken); + } + /// /// Cast the value of this to a . /// @@ -1784,7 +2003,39 @@ internal override int GetDeepHashCode() private void ReadElementFrom(XmlReader r, LoadOptions o) { - if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + ReadElementFromImpl(r, o); + + if (!r.IsEmptyElement) + { + r.Read(); + ReadContentFrom(r, o); + } + + r.Read(); + } + + private async Task ReadElementFromAsync(XmlReader r, LoadOptions o, CancellationToken cancellationTokentoken) + { + ReadElementFromImpl(r, o); + + if (!r.IsEmptyElement) + { + cancellationTokentoken.ThrowIfCancellationRequested(); + await r.ReadAsync().ConfigureAwait(false); + + await ReadContentFromAsync(r, o, cancellationTokentoken).ConfigureAwait(false); + } + + cancellationTokentoken.ThrowIfCancellationRequested(); + await r.ReadAsync().ConfigureAwait(false); + } + + /// + /// Shared implementation between ReadElementFrom / ReadElementFromAsync. + /// + private void ReadElementFromImpl(XmlReader r, LoadOptions o) + { + if(r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); name = XNamespace.Get(r.NamespaceURI).GetName(r.LocalName); if ((o & LoadOptions.SetBaseUri) != 0) { @@ -1816,12 +2067,6 @@ private void ReadElementFrom(XmlReader r, LoadOptions o) } while (r.MoveToNextAttribute()); r.MoveToElement(); } - if (!r.IsEmptyElement) - { - r.Read(); - ReadContentFrom(r, o); - } - r.Read(); } internal void RemoveAttribute(XAttribute a) diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XLinq.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XLinq.cs index 95d4cde8c45a0..5ae8f32a4a6e2 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XLinq.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XLinq.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using IEnumerable = System.Collections.IEnumerable; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; @@ -246,6 +248,51 @@ public void WriteElement(XElement e) } } + public async Task WriteElementAsync(XElement e, CancellationToken cancellationToken) + { + PushAncestors(e); + XElement root = e; + XNode n = e; + while (true) + { + e = n as XElement; + if (e != null) + { + await WriteStartElementAsync(e, cancellationToken).ConfigureAwait(false); + if (e.content == null) + { + await WriteEndElementAsync(cancellationToken).ConfigureAwait(false); + } + else + { + string s = e.content as string; + if (s != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await _writer.WriteStringAsync(s).ConfigureAwait(false); + await WriteFullEndElementAsync(cancellationToken).ConfigureAwait(false); + } + else + { + n = ((XNode) e.content).next; + continue; + } + } + } + else + { + await n.WriteToAsync(_writer, cancellationToken).ConfigureAwait(false); + } + while (n != root && n == n.parent.content) + { + n = n.parent; + await WriteFullEndElementAsync(cancellationToken).ConfigureAwait(false); + } + if (n == root) break; + n = n.next; + } + } + private string GetPrefixOfNamespace(XNamespace ns, bool allowDefaultNamespace) { string namespaceName = ns.NamespaceName; @@ -300,6 +347,13 @@ private void WriteEndElement() _writer.WriteEndElement(); _resolver.PopScope(); } + + private async Task WriteEndElementAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + await _writer.WriteEndElementAsync().ConfigureAwait(false); + _resolver.PopScope(); + } private void WriteFullEndElement() { @@ -307,6 +361,13 @@ private void WriteFullEndElement() _resolver.PopScope(); } + private async Task WriteFullEndElementAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + await _writer.WriteFullEndElementAsync().ConfigureAwait(false); + _resolver.PopScope(); + } + private void WriteStartElement(XElement e) { PushElement(e); @@ -325,6 +386,25 @@ private void WriteStartElement(XElement e) } while (a != e.lastAttr); } } + + async Task WriteStartElementAsync(XElement e, CancellationToken cancellationToken) + { + PushElement(e); + XNamespace ns = e.Name.Namespace; + await _writer.WriteStartElementAsync(GetPrefixOfNamespace(ns, true), e.Name.LocalName, ns.NamespaceName).ConfigureAwait(false); + XAttribute a = e.lastAttr; + if (a != null) + { + do + { + a = a.next; + ns = a.Name.Namespace; + string localName = a.Name.LocalName; + string namespaceName = ns.NamespaceName; + await _writer.WriteAttributeStringAsync(GetPrefixOfNamespace(ns, false), localName, namespaceName.Length == 0 && localName == "xmlns" ? XNamespace.xmlnsPrefixNamespace : namespaceName, a.Value).ConfigureAwait(false); + } while (a != e.lastAttr); + } + } } internal struct NamespaceResolver diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNode.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNode.cs index b4740d5cad0a3..110823dc16f6f 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNode.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNode.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using CultureInfo = System.Globalization.CultureInfo; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; @@ -461,6 +463,72 @@ public static XNode ReadFrom(XmlReader reader) } } + /// + /// Creates an from an . + /// The runtime type of the node is determined by the node type + /// () of the first node encountered + /// in the reader. + /// + /// An positioned at the node to read into this . + /// A cancellation token. + /// An that contains the nodes read from the reader. + /// + /// Thrown if the is not positioned on a recognized node type. + /// + public static Task ReadFromAsync(XmlReader reader, CancellationToken cancellationToken) + { + if (reader == null) + throw new ArgumentNullException(nameof(reader)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return ReadFromAsyncInternal(reader, cancellationToken); + } + + private static async Task ReadFromAsyncInternal(XmlReader reader, CancellationToken cancellationToken) + { + if (reader.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive); + + XNode ret; + + switch (reader.NodeType) + { + case XmlNodeType.Text: + case XmlNodeType.SignificantWhitespace: + case XmlNodeType.Whitespace: + ret = new XText(reader.Value); + break; + case XmlNodeType.CDATA: + ret = new XCData(reader.Value); + break; + case XmlNodeType.Comment: + ret = new XComment(reader.Value); + break; + case XmlNodeType.DocumentType: + var name = reader.Name; + var publicId = reader.GetAttribute("PUBLIC"); + var systemId = reader.GetAttribute("SYSTEM"); + var internalSubset = reader.Value; + + ret = new XDocumentType(name, publicId, systemId, internalSubset); + break; + case XmlNodeType.Element: + return await XElement.CreateAsync(reader, cancellationToken).ConfigureAwait(false); + case XmlNodeType.ProcessingInstruction: + var target = reader.Name; + var data = reader.Value; + + ret = new XProcessingInstruction(target, data); + break; + default: + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnexpectedNodeType, reader.NodeType)); + } + + cancellationToken.ThrowIfCancellationRequested(); + await reader.ReadAsync().ConfigureAwait(false); + + return ret; + } + /// /// Removes this XNode from the underlying XML tree. /// @@ -559,6 +627,13 @@ public static bool DeepEquals(XNode n1, XNode n2) /// The to write the current node into. public abstract void WriteTo(XmlWriter writer); + /// + /// Write the current node to an . + /// + /// The to write the current node into. + /// A cancellation token. + public abstract Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken); + internal virtual void AppendText(StringBuilder sb) { } diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XProcessingInstruction.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XProcessingInstruction.cs index 2cb0214a01cbc..2ad625fc613b8 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XProcessingInstruction.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XProcessingInstruction.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Threading; +using System.Threading.Tasks; + namespace System.Xml.Linq { /// @@ -119,6 +122,22 @@ public override void WriteTo(XmlWriter writer) writer.WriteProcessingInstruction(target, data); } + /// + /// Writes this to the passed in . + /// + /// + /// The to write this to. + /// + /// A cancellation token. + public override Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + return writer.WriteProcessingInstructionAsync(target, data); + } + internal override XNode CloneNode() { return new XProcessingInstruction(this); diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XText.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XText.cs index c31a668d34d6a..7ca06139f0819 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XText.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XText.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using StringBuilder = System.Text.StringBuilder; +using System.Threading; +using System.Threading.Tasks; namespace System.Xml.Linq { @@ -90,6 +92,27 @@ public override void WriteTo(XmlWriter writer) } } + /// + /// Write this to the given . + /// + /// + /// The to write this to. + /// + /// + /// A cancellation token. + /// + public override Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + return parent is XDocument? + writer.WriteWhitespaceAsync(text) : + writer.WriteStringAsync(text); + } + internal override void AppendText(StringBuilder sb) { sb.Append(text); diff --git a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/BridgeHelpers.cs b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/BridgeHelpers.cs index f01688ee2dda5..5185156c8e473 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/BridgeHelpers.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/BridgeHelpers.cs @@ -163,7 +163,7 @@ public XmlReader GetReaderStr(string xml) } } - public string GetTestFileName() + public static string GetTestFileName() { return Path.Combine("TestData", "XmlReader", "API", pGenericXml); } diff --git a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/HelperExtensionMethods.cs b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/HelperExtensionMethods.cs index c74b7eef6691c..f055d72ef96bb 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/HelperExtensionMethods.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/HelperExtensionMethods.cs @@ -12,8 +12,6 @@ using Microsoft.Test.ModuleCore; -[assembly: System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - namespace CoreXml.Test.XLinq { public static class Helpers diff --git a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/XDocument.Common.csproj b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/XDocument.Common.csproj index 7275382504a1f..0eeeb30e440f3 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/XDocument.Common.csproj +++ b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/XDocument.Common.csproj @@ -24,9 +24,6 @@ - - Common\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs - diff --git a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/XDocument.Test.ModuleCore.csproj b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/XDocument.Test.ModuleCore.csproj index 84f5852febef8..b45e771099221 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/XDocument.Test.ModuleCore.csproj +++ b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/XDocument.Test.ModuleCore.csproj @@ -28,9 +28,6 @@ - - Common\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs - diff --git a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/util.cs b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/util.cs index ffb07d3b24577..05b16118319ab 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/util.cs +++ b/src/libraries/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/util.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Generic; -[assembly: System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - namespace Microsoft.Test.ModuleCore { //////////////////////////////////////////////////////////////// diff --git a/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs new file mode 100644 index 0000000000000..0c09af3d86fe1 --- /dev/null +++ b/src/libraries/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs @@ -0,0 +1,206 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; +using XmlCoreTest.Common; +using Xunit; + +namespace CoreXml.Test.XLinq +{ + public class LoadSaveAsyncTests : BridgeHelpers + { + [Fact] + public static void ArgumentValidation() + { + // Verify that ArgumentNullExceptions are thrown when passing null to LoadAsync and SaveAsync + Assert.Throws(() => { XDocument.LoadAsync((XmlReader)null, LoadOptions.None, CancellationToken.None); }); + Assert.Throws(() => { new XDocument().SaveAsync((XmlWriter)null, CancellationToken.None); }); + Assert.Throws(() => { XElement.LoadAsync((XmlReader)null, LoadOptions.None, CancellationToken.None); }); + Assert.Throws(() => { new XElement("Name").SaveAsync((XmlWriter)null, CancellationToken.None); }); + } + + [Fact] + public static async Task AlreadyCanceled() + { + // Verify that providing an already canceled cancellation token will result in a canceled task + + await Assert.ThrowsAnyAsync(() => XDocument.LoadAsync(Stream.Null, LoadOptions.None, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => XDocument.LoadAsync(StreamReader.Null, LoadOptions.None, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => XDocument.LoadAsync(XmlReader.Create(Stream.Null), LoadOptions.None, new CancellationToken(true))); + + await Assert.ThrowsAnyAsync(() => new XDocument().SaveAsync(Stream.Null, SaveOptions.None, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => new XDocument().SaveAsync(StreamWriter.Null, SaveOptions.None, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => new XDocument().SaveAsync(XmlWriter.Create(Stream.Null), new CancellationToken(true))); + + await Assert.ThrowsAnyAsync(() => XElement.LoadAsync(Stream.Null, LoadOptions.None, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => XElement.LoadAsync(StreamReader.Null, LoadOptions.None, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => XElement.LoadAsync(XmlReader.Create(Stream.Null), LoadOptions.None, new CancellationToken(true))); + + await Assert.ThrowsAnyAsync(() => new XElement("Name").SaveAsync(Stream.Null, SaveOptions.None, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => new XElement("Name").SaveAsync(StreamWriter.Null, SaveOptions.None, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => new XElement("Name").SaveAsync(XmlWriter.Create(Stream.Null), new CancellationToken(true))); + } + + [Theory] + [MemberData("RoundtripOptions_MemberData")] + public static async Task RoundtripSyncAsyncMatches_XmlReader(bool document, LoadOptions loadOptions, SaveOptions saveOptions) + { + // Create reader and writer settings + var readerSettings = new XmlReaderSettings(); + var writerSettings = new XmlWriterSettings(); + if ((saveOptions & SaveOptions.OmitDuplicateNamespaces) != 0) + { + writerSettings.NamespaceHandling = NamespaceHandling.OmitDuplicates; + } + if ((saveOptions & SaveOptions.DisableFormatting) != 0) + { + writerSettings.Indent = false; + writerSettings.NewLineHandling = NewLineHandling.None; + } + + // Roundtrip XML using synchronous and XmlReader/Writer + MemoryStream syncOutput = new MemoryStream(); + using (XmlReader syncReader = XmlReader.Create(FilePathUtil.getStream(GetTestFileName()), readerSettings)) + using (XmlWriter syncWriter = XmlWriter.Create(syncOutput, writerSettings)) + { + if (document) + { + XDocument syncDoc = XDocument.Load(syncReader, loadOptions); + syncDoc.Save(syncWriter); + } + else + { + XElement syncElement = XElement.Load(syncReader, loadOptions); + syncElement.Save(syncWriter); + } + } + + // Roundtrip XML using asynchronous and XmlReader/Writer + readerSettings.Async = true; + writerSettings.Async = true; + MemoryStream asyncOutput = new MemoryStream(); + using (XmlReader asyncReader = XmlReader.Create(FilePathUtil.getStream(GetTestFileName()), readerSettings)) + using (XmlWriter asyncWriter = XmlWriter.Create(asyncOutput, writerSettings)) + { + if (document) + { + XDocument asyncDoc = await XDocument.LoadAsync(asyncReader, loadOptions, CancellationToken.None); + await asyncDoc.SaveAsync(asyncWriter, CancellationToken.None); + } + else + { + XElement asyncElement = await XElement.LoadAsync(asyncReader, loadOptions, CancellationToken.None); + await asyncElement.SaveAsync(asyncWriter, CancellationToken.None); + } + } + + // Compare to make sure the synchronous and asynchronous results are the same + Assert.Equal(syncOutput.ToArray(), asyncOutput.ToArray()); + } + + [Theory] + [MemberData("RoundtripOptions_MemberData")] + public static async Task RoundtripSyncAsyncMatches_StreamReader(bool document, LoadOptions loadOptions, SaveOptions saveOptions) + { + // Roundtrip XML using synchronous and StreamReader/Writer + MemoryStream syncOutput = new MemoryStream(); + using (StreamReader syncReader = new StreamReader(FilePathUtil.getStream(GetTestFileName()))) + using (StreamWriter syncWriter = new StreamWriter(syncOutput)) + { + if (document) + { + XDocument syncDoc = XDocument.Load(syncReader, loadOptions); + syncDoc.Save(syncWriter, saveOptions); + } + else + { + XElement syncElement = XElement.Load(syncReader, loadOptions); + syncElement.Save(syncWriter, saveOptions); + } + } + + // Roundtrip XML using asynchronous and StreamReader/Writer + MemoryStream asyncOutput = new MemoryStream(); + using (StreamReader asyncReader = new StreamReader(FilePathUtil.getStream(GetTestFileName()))) + using (StreamWriter asyncWriter = new StreamWriter(asyncOutput)) + { + if (document) + { + XDocument asyncDoc = await XDocument.LoadAsync(asyncReader, loadOptions, CancellationToken.None); + await asyncDoc.SaveAsync(asyncWriter, saveOptions, CancellationToken.None); + } + else + { + XElement asyncElement = await XElement.LoadAsync(asyncReader, loadOptions, CancellationToken.None); + await asyncElement.SaveAsync(asyncWriter, saveOptions, CancellationToken.None); + } + } + + // Compare to make sure the synchronous and asynchronous results are the same + Assert.Equal(syncOutput.ToArray(), asyncOutput.ToArray()); + } + + [Theory] + [MemberData("RoundtripOptions_MemberData")] + public static async Task RoundtripSyncAsyncMatches_Stream(bool document, LoadOptions loadOptions, SaveOptions saveOptions) + { + // Roundtrip XML using synchronous and Stream + MemoryStream syncOutput = new MemoryStream(); + using (Stream syncStream = FilePathUtil.getStream(GetTestFileName())) + { + if (document) + { + XDocument syncDoc = XDocument.Load(syncStream, loadOptions); + syncDoc.Save(syncOutput, saveOptions); + } + else + { + XElement syncElement = XElement.Load(syncStream, loadOptions); + syncElement.Save(syncOutput, saveOptions); + } + } + + // Roundtrip XML using asynchronous and Stream + MemoryStream asyncOutput = new MemoryStream(); + using (Stream asyncStream = FilePathUtil.getStream(GetTestFileName())) + { + if (document) + { + XDocument asyncDoc = await XDocument.LoadAsync(asyncStream, loadOptions, CancellationToken.None); + await asyncDoc.SaveAsync(asyncOutput, saveOptions, CancellationToken.None); + } + else + { + XElement asyncElement = await XElement.LoadAsync(asyncStream, loadOptions, CancellationToken.None); + await asyncElement.SaveAsync(asyncOutput, saveOptions, CancellationToken.None); + } + } + + // Compare to make sure the synchronous and asynchronous results are the same + Assert.Equal(syncOutput.ToArray(), asyncOutput.ToArray()); + } + + // Inputs to the Roundtrip* tests: + // - Boolean for whether to test XDocument (true) or XElement (false) + // - LoadOptions value + // - SaveOptions value + public static IEnumerable RoundtripOptions_MemberData + { + get + { + foreach (bool doc in new[] { true, false }) + foreach (LoadOptions loadOptions in Enum.GetValues(typeof(LoadOptions))) + foreach (SaveOptions saveOptions in Enum.GetValues(typeof(SaveOptions))) + yield return new object[] { doc, loadOptions, saveOptions }; + } + } + + } +} \ No newline at end of file diff --git a/src/libraries/System.Private.Xml.Linq/tests/misc/System.Xml.Linq.Misc.Tests.csproj b/src/libraries/System.Private.Xml.Linq/tests/misc/System.Xml.Linq.Misc.Tests.csproj index 4e06e73275c69..36a4365c19432 100644 --- a/src/libraries/System.Private.Xml.Linq/tests/misc/System.Xml.Linq.Misc.Tests.csproj +++ b/src/libraries/System.Private.Xml.Linq/tests/misc/System.Xml.Linq.Misc.Tests.csproj @@ -7,7 +7,7 @@ {35FA1FA9-A504-4B9E-93F0-E5D03C21BECA} Library System.Xml.Linq.Misc.Tests - .NETStandard,Version=v1.3 + .NETStandard,Version=v1.7 System.Xml.Linq.Tests @@ -23,12 +23,15 @@ + + + diff --git a/src/libraries/System.Xml.XDocument/ref/System.Xml.XDocument.cs b/src/libraries/System.Xml.XDocument/ref/System.Xml.XDocument.cs index 26f13b069ea5c..67eacced90660 100644 --- a/src/libraries/System.Xml.XDocument/ref/System.Xml.XDocument.cs +++ b/src/libraries/System.Xml.XDocument/ref/System.Xml.XDocument.cs @@ -121,6 +121,9 @@ public XCData(string value) : base(default(string)) { } public XCData(System.Xml.Linq.XCData other) : base(default(string)) { } public override System.Xml.XmlNodeType NodeType { get { throw null; } } public override void WriteTo(System.Xml.XmlWriter writer) { } +#if netcoreapp11 + public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif } public partial class XComment : System.Xml.Linq.XNode { @@ -129,6 +132,9 @@ public XComment(System.Xml.Linq.XComment other) { } public override System.Xml.XmlNodeType NodeType { get { throw null; } } public string Value { get { throw null; } set { } } public override void WriteTo(System.Xml.XmlWriter writer) { } +#if netcoreapp11 + public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif } public abstract partial class XContainer : System.Xml.Linq.XNode { @@ -178,6 +184,11 @@ public XDocument(System.Xml.Linq.XDocument other) { } public static System.Xml.Linq.XDocument Load(string uri, System.Xml.Linq.LoadOptions options) { throw null; } public static System.Xml.Linq.XDocument Load(System.Xml.XmlReader reader) { throw null; } public static System.Xml.Linq.XDocument Load(System.Xml.XmlReader reader, System.Xml.Linq.LoadOptions options) { throw null; } +#if netcoreapp11 + public static System.Threading.Tasks.Task LoadAsync(System.IO.Stream stream, System.Xml.Linq.LoadOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } + public static System.Threading.Tasks.Task LoadAsync(System.IO.TextReader textReader, System.Xml.Linq.LoadOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } + public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader, System.Xml.Linq.LoadOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif public static System.Xml.Linq.XDocument Parse(string text) { throw null; } public static System.Xml.Linq.XDocument Parse(string text, System.Xml.Linq.LoadOptions options) { throw null; } public void Save(System.IO.Stream stream) { } @@ -187,7 +198,15 @@ public void Save(System.IO.TextWriter textWriter, System.Xml.Linq.SaveOptions op public void Save(System.Xml.XmlWriter writer) { } public void Save(System.String fileName) { } public void Save(System.String fileName, System.Xml.Linq.SaveOptions options) { } +#if netcoreapp11 + public System.Threading.Tasks.Task SaveAsync(System.IO.Stream stream, System.Xml.Linq.SaveOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.Task SaveAsync(System.IO.TextWriter textWriter, System.Xml.Linq.SaveOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.Task SaveAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif public override void WriteTo(System.Xml.XmlWriter writer) { } +#if netcoreapp11 + public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif } public partial class XDocumentType : System.Xml.Linq.XNode { @@ -199,6 +218,9 @@ public XDocumentType(System.Xml.Linq.XDocumentType other) { } public string PublicId { get { throw null; } set { } } public string SystemId { get { throw null; } set { } } public override void WriteTo(System.Xml.XmlWriter writer) { } +#if netcoreapp11 + public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif } public partial class XElement : System.Xml.Linq.XContainer, System.Xml.Serialization.IXmlSerializable { @@ -235,6 +257,11 @@ public XElement(System.Xml.Linq.XStreamingElement other) { } public static System.Xml.Linq.XElement Load(string uri, System.Xml.Linq.LoadOptions options) { throw null; } public static System.Xml.Linq.XElement Load(System.Xml.XmlReader reader) { throw null; } public static System.Xml.Linq.XElement Load(System.Xml.XmlReader reader, System.Xml.Linq.LoadOptions options) { throw null; } +#if netcoreapp11 + public static System.Threading.Tasks.Task LoadAsync(System.IO.Stream stream, System.Xml.Linq.LoadOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } + public static System.Threading.Tasks.Task LoadAsync(System.IO.TextReader textReader, System.Xml.Linq.LoadOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } + public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader, System.Xml.Linq.LoadOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif [System.CLSCompliantAttribute(false)] public static explicit operator bool (System.Xml.Linq.XElement element) { throw null; } [System.CLSCompliantAttribute(false)] @@ -300,6 +327,11 @@ public void Save(System.IO.TextWriter textWriter, System.Xml.Linq.SaveOptions op public void Save(System.Xml.XmlWriter writer) { } public void Save(System.String fileName) { } public void Save(System.String fileName, System.Xml.Linq.SaveOptions options) { } +#if netcoreapp11 + public System.Threading.Tasks.Task SaveAsync(System.IO.Stream stream, System.Xml.Linq.SaveOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.Task SaveAsync(System.IO.TextWriter textWriter, System.Xml.Linq.SaveOptions options, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.Task SaveAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif public void SetAttributeValue(System.Xml.Linq.XName name, object value) { } public void SetElementValue(System.Xml.Linq.XName name, object value) { } public void SetValue(object value) { } @@ -307,6 +339,9 @@ public void SetValue(object value) { } void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } public override void WriteTo(System.Xml.XmlWriter writer) { } +#if netcoreapp11 + public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif } public sealed partial class XName : System.IEquatable, System.Runtime.Serialization.ISerializable { @@ -370,12 +405,18 @@ public void AddBeforeSelf(params object[] content) { } public System.Collections.Generic.IEnumerable NodesAfterSelf() { throw null; } public System.Collections.Generic.IEnumerable NodesBeforeSelf() { throw null; } public static System.Xml.Linq.XNode ReadFrom(System.Xml.XmlReader reader) { throw null; } +#if netcoreapp11 + public static System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif public void Remove() { } public void ReplaceWith(object content) { } public void ReplaceWith(params object[] content) { } public override string ToString() { throw null; } public string ToString(System.Xml.Linq.SaveOptions options) { throw null; } public abstract void WriteTo(System.Xml.XmlWriter writer); +#if netcoreapp11 + public abstract System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken); +#endif } public sealed partial class XNodeDocumentOrderComparer : System.Collections.Generic.IComparer, System.Collections.IComparer { @@ -435,6 +476,9 @@ public XProcessingInstruction(System.Xml.Linq.XProcessingInstruction other) { } public override System.Xml.XmlNodeType NodeType { get { throw null; } } public string Target { get { throw null; } set { } } public override void WriteTo(System.Xml.XmlWriter writer) { } +#if netcoreapp11 + public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif } public partial class XStreamingElement { @@ -462,6 +506,9 @@ public XText(System.Xml.Linq.XText other) { } public override System.Xml.XmlNodeType NodeType { get { throw null; } } public string Value { get { throw null; } set { } } public override void WriteTo(System.Xml.XmlWriter writer) { } +#if netcoreapp11 + public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken cancellationToken) { throw null; } +#endif } } namespace System.Xml.Schema diff --git a/src/libraries/System.Xml.XDocument/ref/System.Xml.XDocument.csproj b/src/libraries/System.Xml.XDocument/ref/System.Xml.XDocument.csproj index 20dd5717d9233..2a039d6439481 100644 --- a/src/libraries/System.Xml.XDocument/ref/System.Xml.XDocument.csproj +++ b/src/libraries/System.Xml.XDocument/ref/System.Xml.XDocument.csproj @@ -4,6 +4,7 @@ Library .NETStandard,Version=v1.7 + $(DefineConstants);netcoreapp11 diff --git a/src/libraries/System.Xml.XDocument/src/ApiCompatBaseline.net463.txt b/src/libraries/System.Xml.XDocument/src/ApiCompatBaseline.net463.txt new file mode 100644 index 0000000000000..bf1bfce8ae272 --- /dev/null +++ b/src/libraries/System.Xml.XDocument/src/ApiCompatBaseline.net463.txt @@ -0,0 +1,23 @@ +Compat issues with assembly System.Xml.XDocument: +MembersMustExist : Member 'System.Xml.Linq.XCData.WriteToAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XComment.WriteToAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XDocument.LoadAsync(System.IO.Stream, System.Xml.Linq.LoadOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XDocument.LoadAsync(System.IO.TextReader, System.Xml.Linq.LoadOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XDocument.LoadAsync(System.Xml.XmlReader, System.Xml.Linq.LoadOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XDocument.SaveAsync(System.IO.Stream, System.Xml.Linq.SaveOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XDocument.SaveAsync(System.IO.TextWriter, System.Xml.Linq.SaveOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XDocument.SaveAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XDocument.WriteToAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XDocumentType.WriteToAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XElement.LoadAsync(System.IO.Stream, System.Xml.Linq.LoadOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XElement.LoadAsync(System.IO.TextReader, System.Xml.Linq.LoadOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XElement.LoadAsync(System.Xml.XmlReader, System.Xml.Linq.LoadOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XElement.SaveAsync(System.IO.Stream, System.Xml.Linq.SaveOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XElement.SaveAsync(System.IO.TextWriter, System.Xml.Linq.SaveOptions, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XElement.SaveAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XElement.WriteToAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XNode.ReadFromAsync(System.Xml.XmlReader, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XNode.WriteToAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XProcessingInstruction.WriteToAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.Xml.Linq.XText.WriteToAsync(System.Xml.XmlWriter, System.Threading.CancellationToken)' does not exist in the implementation but it does exist in the contract. +Total Issues: 21 \ No newline at end of file