Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9bf718b
WriteStartDocumentAsync works
mikeebowen Mar 14, 2025
f71c2f9
Merge branch 'main' into add-async-xmlwriter-methods-913
mikeebowen Mar 17, 2025
7b722f8
declare FEATURE_ASYNC_SAX_XML constant
mikeebowen Mar 17, 2025
8079fe5
Merge remote-tracking branch 'upstream/main' into add-async-xmlwriter…
mikeebowen Mar 17, 2025
095973f
add missing OpenXmlPartWriter constructors
mikeebowen Mar 17, 2025
aeb8c57
add abstract methods to OpenXmlWriter
mikeebowen Mar 17, 2025
b409310
reorder PublicAPI.Shipped.txt
mikeebowen Mar 17, 2025
46bada0
reorder methods
mikeebowen Mar 17, 2025
e3e30d3
reorder methods in OpenXmlPartWriter
mikeebowen Mar 17, 2025
bf67eb8
add unimplemented async methods
mikeebowen Mar 18, 2025
19eaef9
Add tests
mikeebowen Mar 18, 2025
2e0636f
add more tests
mikeebowen Mar 18, 2025
94ef2cc
rename tests
mikeebowen Mar 19, 2025
6e5e262
remove async methods that use an OpenXmlReader
mikeebowen Mar 19, 2025
b3ff0d6
add #if FEATURE_ASYNC_SAX_XML to PublicAPI.shipped for OpenXmlPartWri…
mikeebowen Mar 20, 2025
602ad1e
use version specific PublicAPI.shipped.txt files
mikeebowen Mar 20, 2025
83613f0
update PublishAPI.shipped files and change abstract methods to virtual
mikeebowen Mar 20, 2025
622c3f6
revert PublicAPI.Shipped.txt to match main
mikeebowen Mar 20, 2025
2875ad6
make tests use equal instead of contains
mikeebowen Mar 21, 2025
e07449c
use OpenXmlPartWriterSettings for async settings
mikeebowen Mar 24, 2025
5f1d125
add blank lines for readability
mikeebowen Mar 24, 2025
0b03d26
use async method
mikeebowen Mar 24, 2025
0421110
remove unnecessary async and add default sync implementations
mikeebowen Mar 25, 2025
6c3a0a0
Merge remote-tracking branch 'upstream/main' into add-async-xmlwriter…
mikeebowen Mar 25, 2025
e97b0b5
remove removed methods from PublicAPI.Shipped.txt
mikeebowen Mar 25, 2025
c2b9513
only hide Async in OpenXmlPartWriter constructors from older versions…
mikeebowen Mar 25, 2025
0ec2368
remove repeated calls to ThrowIfObjectDisposed
mikeebowen Mar 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
<LatestTargetFramework>net8.0</LatestTargetFramework>
<SamplesFrameworks>net8.0</SamplesFrameworks>
<SamplesFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(SamplesFrameworks);net472</SamplesFrameworks>
<DefineConstants Condition=" '$(TargetFramework)' != 'net35' And '$(TargetFramework)' != 'net40' And '$(TargetFramework)' != 'net46' And '$(TargetFramework)' != 'net472' ">$(DefineConstants);FEATURE_ASYNC_SAX_XML</DefineConstants>
</PropertyGroup>
</Otherwise>
</Choose>
Expand Down
292 changes: 286 additions & 6 deletions src/DocumentFormat.OpenXml.Framework/OpenXmlPartWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
#if FEATURE_ASYNC_SAX_XML
using System.Threading.Tasks;
#endif
using System.Xml;

namespace DocumentFormat.OpenXml
Expand All @@ -29,6 +32,30 @@ public OpenXmlPartWriter(OpenXmlPart openXmlPart)
{
}

#if FEATURE_ASYNC_SAX_XML
/// <summary>
/// Initializes a new instance of the OpenXmlPartWriter.
/// </summary>
/// <param name="openXmlPart">The OpenXmlPart to be written to.</param>
/// <param name="useAsync">Whether the writer should be initialized using async</param>
public OpenXmlPartWriter(OpenXmlPart openXmlPart, bool useAsync)
{
if (openXmlPart is null)
{
throw new ArgumentNullException(nameof(openXmlPart));
}

var partStream = openXmlPart.GetStream(FileMode.Create);
var settings = new XmlWriterSettings
{
CloseOutput = true,
Async = useAsync,
};

_xmlWriter = XmlWriter.Create(partStream, settings);
}
#endif

/// <summary>
/// Initializes a new instance of the OpenXmlPartWriter.
/// </summary>
Expand Down Expand Up @@ -56,6 +83,37 @@ public OpenXmlPartWriter(OpenXmlPart openXmlPart, Encoding encoding)
_xmlWriter = XmlWriter.Create(partStream, settings);
}

#if FEATURE_ASYNC_SAX_XML
/// <summary>
/// Initializes a new instance of the OpenXmlPartWriter.
/// </summary>
/// <param name="openXmlPart">The OpenXmlPart to be written to.</param>
/// <param name="encoding">The encoding for the XML stream.</param>
/// <param name="useAsync">Whether the writer should be initialized using async</param>
public OpenXmlPartWriter(OpenXmlPart openXmlPart, Encoding encoding, bool useAsync)
Comment thread
twsouthwick marked this conversation as resolved.
Outdated
{
if (openXmlPart is null)
{
throw new ArgumentNullException(nameof(openXmlPart));
}

if (encoding is null)
{
throw new ArgumentNullException(nameof(encoding));
}

var partStream = openXmlPart.GetStream(FileMode.Create);
var settings = new XmlWriterSettings
{
CloseOutput = true,
Encoding = encoding,
Async = useAsync,
};

_xmlWriter = XmlWriter.Create(partStream, settings);
}
#endif

/// <summary>
/// Initializes a new instance of the OpenXmlPartWriter.
/// </summary>
Expand All @@ -65,6 +123,30 @@ public OpenXmlPartWriter(Stream partStream)
{
}

#if FEATURE_ASYNC_SAX_XML
/// <summary>
/// Initializes a new instance of the OpenXmlPartWriter.
/// </summary>
/// <param name="partStream">The given part stream.</param>
/// <param name="useAsync">Whether the writer should be initialized using async</param>
public OpenXmlPartWriter(Stream partStream, bool useAsync)
{
if (partStream is null)
{
throw new ArgumentNullException(nameof(partStream));
}

var settings = new XmlWriterSettings
{
CloseOutput = false,
Encoding = Encoding.UTF8,
Async = useAsync,
};

_xmlWriter = XmlWriter.Create(partStream, settings);
}
#endif

/// <summary>
/// Initializes a new instance of the OpenXmlPartWriter.
/// </summary>
Expand All @@ -91,6 +173,36 @@ public OpenXmlPartWriter(Stream partStream, Encoding encoding)
_xmlWriter = XmlWriter.Create(partStream, settings);
}

#if FEATURE_ASYNC_SAX_XML
/// <summary>
/// Initializes a new instance of the OpenXmlPartWriter.
/// </summary>
/// <param name="partStream">The given part stream.</param>
/// <param name="encoding">The encoding for the XML stream.</param>
/// <param name="useAsync">Whether the writer should be initialized using async</param>
public OpenXmlPartWriter(Stream partStream, Encoding encoding, bool useAsync)
{
if (partStream is null)
{
throw new ArgumentNullException(nameof(partStream));
}

if (encoding is null)
{
throw new ArgumentNullException(nameof(encoding));
}

var settings = new XmlWriterSettings
{
CloseOutput = false,
Encoding = encoding,
Async = useAsync,
};

_xmlWriter = XmlWriter.Create(partStream, settings);
}
#endif

#region public OpenXmlWriter methods

/// <summary>
Expand All @@ -99,7 +211,6 @@ public OpenXmlPartWriter(Stream partStream, Encoding encoding)
public override void WriteStartDocument()
{
ThrowIfObjectDisposed();

_xmlWriter.WriteStartDocument();
}

Expand All @@ -110,7 +221,6 @@ public override void WriteStartDocument()
public override void WriteStartDocument(bool standalone)
{
ThrowIfObjectDisposed();
Comment thread
twsouthwick marked this conversation as resolved.

_xmlWriter.WriteStartDocument(standalone);
}

Expand Down Expand Up @@ -307,9 +417,7 @@ public override void WriteStartElement(OpenXmlElement elementObject, IEnumerable
public override void WriteEndElement()
{
ThrowIfObjectDisposed();

_xmlWriter.WriteEndElement();

_isLeafTextElementStart = false;
}

Expand Down Expand Up @@ -345,9 +453,7 @@ public override void WriteElement(OpenXmlElement elementObject)
}

ThrowIfObjectDisposed();

elementObject.WriteTo(_xmlWriter);

_isLeafTextElementStart = false;
}

Expand All @@ -365,5 +471,179 @@ public override void Close()
}

#endregion

// Async Methods
#if FEATURE_ASYNC_SAX_XML
/// <summary>
/// Asynchronously writes the XML declaration with the version "1.0".
/// </summary>
public async override Task WriteStartDocumentAsync()
{
ThrowIfObjectDisposed();
await _xmlWriter.WriteStartDocumentAsync().ConfigureAwait(true);
Comment thread
twsouthwick marked this conversation as resolved.
Outdated
return;
}
Comment thread
twsouthwick marked this conversation as resolved.
Outdated

/// <summary>
/// Asynchronously writes the XML declaration with the version "1.0" and the standalone attribute.
/// </summary>
/// <param name="standalone">If true, it writes "standalone=yes"; if false, it writes "standalone=no". </param>
public async override Task WriteStartDocumentAsync(bool standalone)
{
ThrowIfObjectDisposed();
await _xmlWriter.WriteStartDocumentAsync(standalone).ConfigureAwait(true);
return;
}

/// <summary>
/// Asynchronously writes out a start tag of the element and all the attributes of the element.
/// </summary>
/// <param name="elementObject">The OpenXmlElement object to be written.</param>
public async override Task WriteStartElementAsync(OpenXmlElement elementObject)
{
if (elementObject is null)
{
throw new ArgumentNullException(nameof(elementObject));
}

if (elementObject is OpenXmlMiscNode)
{
throw new ArgumentOutOfRangeException(nameof(elementObject));
}

ThrowIfObjectDisposed();

_xmlWriter.WriteStartElement(elementObject.Prefix, elementObject.LocalName, elementObject.NamespaceUri);
Comment thread
twsouthwick marked this conversation as resolved.
Outdated

if (elementObject.HasAttributes)
{
// write attributes
foreach (var attribute in elementObject.GetAttributes())
{
await _xmlWriter.WriteAttributeStringAsync(attribute.Prefix, attribute.LocalName, attribute.NamespaceUri, attribute.Value).ConfigureAwait(true);
}
}

if (elementObject is OpenXmlLeafTextElement)
{
_isLeafTextElementStart = true;
}
else
{
_isLeafTextElementStart = false;
}
}

/// <summary>
/// Asynchronously writes out a start tag of the element. And write the attributes in attributes. The attributes of the element will be omitted.
/// </summary>
/// <param name="elementObject">The OpenXmlElement object to be written.</param>
/// <param name="attributes">The attributes to be written.</param>
public async override Task WriteStartElementAsync(OpenXmlElement elementObject, IEnumerable<OpenXmlAttribute> attributes)
{
if (elementObject is null)
{
throw new ArgumentNullException(nameof(elementObject));
}

await WriteStartElementAsync(elementObject, attributes, elementObject.NamespaceDeclarations).ConfigureAwait(true);
}

/// <summary>
/// Asynchronously writes out a start tag of the element. And write the attributes in attributes. The attributes of the element will be omitted.
/// </summary>
/// <param name="elementObject">The OpenXmlElement object to be written.</param>
/// <param name="attributes">The attributes to be written.</param>
/// <param name="namespaceDeclarations">The namespace declarations to be written, can be null if no namespace declarations.</param>
public async override Task WriteStartElementAsync(OpenXmlElement elementObject, IEnumerable<OpenXmlAttribute> attributes, IEnumerable<KeyValuePair<string, string>> namespaceDeclarations)
{
if (elementObject is null)
{
throw new ArgumentNullException(nameof(elementObject));
}

if (elementObject is OpenXmlMiscNode)
{
throw new ArgumentOutOfRangeException(nameof(elementObject));
}

ThrowIfObjectDisposed();

await _xmlWriter.WriteStartElementAsync(elementObject.Prefix, elementObject.LocalName, elementObject.NamespaceUri).ConfigureAwait(true);

if (namespaceDeclarations is not null)
{
foreach (var item in namespaceDeclarations)
{
await _xmlWriter.WriteAttributeStringAsync(OpenXmlElementContext.XmlnsPrefix, item.Key, OpenXmlElementContext.XmlnsUri, item.Value).ConfigureAwait(true);
}
}

if (attributes is not null)
{
// write attributes
foreach (var attribute in attributes)
{
await _xmlWriter.WriteAttributeStringAsync(attribute.Prefix, attribute.LocalName, attribute.NamespaceUri, attribute.Value).ConfigureAwait(true);
}
}

if (elementObject is OpenXmlLeafTextElement)
{
_isLeafTextElementStart = true;
}
else
{
_isLeafTextElementStart = false;
}
}

/// <summary>
/// Asynchronously closes one element.
/// </summary>
public async override Task WriteEndElementAsync()
{
ThrowIfObjectDisposed();
await _xmlWriter.WriteEndElementAsync().ConfigureAwait(true);
_isLeafTextElementStart = false;
}

/// <summary>
/// Asynchronously writes the OpenXmlElement to the writer.
/// </summary>
/// <param name="elementObject">The OpenXmlElement object to be written.</param>
public async override Task WriteElementAsync(OpenXmlElement elementObject)
{
if (elementObject is null)
{
throw new ArgumentNullException(nameof(elementObject));
}

ThrowIfObjectDisposed();
await WriteStartElementAsync(elementObject).ConfigureAwait(true);
await WriteEndElementAsync().ConfigureAwait(true);
_isLeafTextElementStart = false;
}

/// <summary>
/// Asynchronously writes the given text content.
/// </summary>
/// <param name="text">The text to be written. </param>
public async override Task WriteStringAsync(string text)
{
ThrowIfObjectDisposed();

if (_isLeafTextElementStart)
{
await _xmlWriter.WriteStringAsync(text).ConfigureAwait(true);
}
else
{
throw new InvalidOperationException(ExceptionMessages.InvalidWriteStringCall);
}

// can continue WriteStringAsync(), so don't set _isLeafTextElementStart to false.
}
#endif
}
}
Loading
Loading