diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs index 7466fbeb78e302..868e94109a8f5c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs @@ -34,7 +34,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac internal override bool OnTryWrite(Utf8JsonWriter writer, TAsyncEnumerable value, JsonSerializerOptions options, ref WriteStack state) { - if (!state.SupportContinuation) + if (!state.SupportAsync) { ThrowHelper.ThrowNotSupportedException_TypeRequiresAsyncSerialization(TypeToConvert); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs index 2036aaaae3f95d..898ed9a03041f9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonResumableConverterOfT.cs @@ -25,7 +25,7 @@ public sealed override void Write(Utf8JsonWriter writer, T value, JsonSerializer // Bridge from resumable to value converters. WriteStack state = default; - state.Initialize(typeof(T), options, supportContinuation: false); + state.Initialize(typeof(T), options, supportContinuation: false, supportAsync: false); try { TryWrite(writer, value, options, ref state); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs index 607cb383cbaace..d756b546725b01 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs @@ -64,7 +64,7 @@ jsonTypeInfo is not JsonTypeInfo || "Incorrect method called. WriteUsingGeneratedSerializer() should have been called instead."); WriteStack state = default; - state.Initialize(jsonTypeInfo, supportContinuation: false); + state.Initialize(jsonTypeInfo, supportContinuation: false, supportAsync: false); JsonConverter converter = jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase; Debug.Assert(converter != null); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs index bc71fa13dec703..3690f85696a8eb 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs @@ -256,7 +256,7 @@ private static async Task WriteStreamAsync( using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions)) { WriteStack state = new WriteStack { CancellationToken = cancellationToken }; - JsonConverter converter = state.Initialize(jsonTypeInfo, supportContinuation: true); + JsonConverter converter = state.Initialize(jsonTypeInfo, supportContinuation: true, supportAsync: true); bool isFinalBlock; @@ -329,7 +329,7 @@ private static void WriteStream( using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions)) { WriteStack state = default; - JsonConverter converter = state.Initialize(jsonTypeInfo, supportContinuation: true); + JsonConverter converter = state.Initialize(jsonTypeInfo, supportContinuation: true, supportAsync: false); bool isFinalBlock; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs index a73532d0187b86..616044f7f0d213 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/WriteStack.cs @@ -78,6 +78,11 @@ internal struct WriteStack /// public bool SupportContinuation; + /// + /// Internal flag indicating that async serialization is supported. Implies `SupportContinuation`. + /// + public bool SupportAsync; + /// /// Stores a reference id that has been calculated for a newly serialized object. /// @@ -98,14 +103,16 @@ private void EnsurePushCapacity() /// /// Initialize the state without delayed initialization of the JsonTypeInfo. /// - public JsonConverter Initialize(Type type, JsonSerializerOptions options, bool supportContinuation) + public JsonConverter Initialize(Type type, JsonSerializerOptions options, bool supportContinuation, bool supportAsync) { JsonTypeInfo jsonTypeInfo = options.GetOrAddJsonTypeInfoForRootType(type); - return Initialize(jsonTypeInfo, supportContinuation); + return Initialize(jsonTypeInfo, supportContinuation, supportAsync); } - internal JsonConverter Initialize(JsonTypeInfo jsonTypeInfo, bool supportContinuation) + internal JsonConverter Initialize(JsonTypeInfo jsonTypeInfo, bool supportContinuation, bool supportAsync) { + Debug.Assert(!supportAsync || supportContinuation, "supportAsync implies supportContinuation."); + Current.JsonTypeInfo = jsonTypeInfo; Current.JsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo; Current.NumberHandling = Current.JsonPropertyInfo.NumberHandling; @@ -118,6 +125,7 @@ internal JsonConverter Initialize(JsonTypeInfo jsonTypeInfo, bool supportContinu } SupportContinuation = supportContinuation; + SupportAsync = supportAsync; return jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase; } diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs index 1f3fd1c159faec..03677695967bce 100644 --- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs @@ -18,7 +18,7 @@ public abstract partial class CollectionTests [MemberData(nameof(GetAsyncEnumerableSources))] public async Task WriteRootLevelAsyncEnumerable(IEnumerable source, int delayInterval, int bufferSize) { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -43,7 +43,7 @@ public async Task WriteRootLevelAsyncEnumerable(IEnumerable [MemberData(nameof(GetAsyncEnumerableSources))] public async Task WriteNestedAsyncEnumerable(IEnumerable source, int delayInterval, int bufferSize) { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -68,7 +68,7 @@ public async Task WriteNestedAsyncEnumerable(IEnumerable sou [MemberData(nameof(GetAsyncEnumerableSources))] public async Task WriteNestedAsyncEnumerable_DTO(IEnumerable source, int delayInterval, int bufferSize) { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -93,7 +93,7 @@ public async Task WriteNestedAsyncEnumerable_DTO(IEnumerable [MemberData(nameof(GetAsyncEnumerableSources))] public async Task WriteNestedAsyncEnumerable_Nullable(IEnumerable source, int delayInterval, int bufferSize) { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -151,7 +151,7 @@ public class AsyncEnumerableDto [MemberData(nameof(GetAsyncEnumerableSources))] public async Task WriteSequentialNestedAsyncEnumerables(IEnumerable source, int delayInterval, int bufferSize) { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -176,7 +176,7 @@ public async Task WriteSequentialNestedAsyncEnumerables(IEnumerable(IEnumerable source, int delayInterval, int bufferSize) { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -209,6 +209,7 @@ public void WriteRootLevelAsyncEnumerableSync_ThrowsNotSupportedException() { IAsyncEnumerable asyncEnumerable = new MockedAsyncEnumerable(Enumerable.Range(1, 10)); Assert.Throws(() => JsonSerializer.Serialize(asyncEnumerable)); + Assert.Throws(() => JsonSerializer.Serialize(new MemoryStream(), asyncEnumerable)); } [Fact] @@ -216,12 +217,13 @@ public void WriteNestedAsyncEnumerableSync_ThrowsNotSupportedException() { IAsyncEnumerable asyncEnumerable = new MockedAsyncEnumerable(Enumerable.Range(1, 10)); Assert.Throws(() => JsonSerializer.Serialize(new { Data = asyncEnumerable })); + Assert.Throws(() => JsonSerializer.Serialize(new MemoryStream(), new { Data = asyncEnumerable })); } [Fact] public async Task WriteAsyncEnumerable_ElementSerializationThrows_ShouldDisposeEnumerator() { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -243,7 +245,7 @@ static IEnumerable ThrowingEnumerable() [Fact] public async Task ReadRootLevelAsyncEnumerable() { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -257,7 +259,7 @@ public async Task ReadRootLevelAsyncEnumerable() [Fact] public async Task ReadNestedAsyncEnumerable() { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -271,7 +273,7 @@ public async Task ReadNestedAsyncEnumerable() [Fact] public async Task ReadAsyncEnumerableOfAsyncEnumerables() { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -289,7 +291,7 @@ public async Task ReadAsyncEnumerableOfAsyncEnumerables() [Fact] public async Task ReadRootLevelAsyncEnumerableDerivative_ThrowsNotSupportedException() { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -314,7 +316,7 @@ public static IEnumerable GetAsyncEnumerableSources() [Fact] public async Task RegressionTest_DisposingEnumeratorOnPendingMoveNextAsyncOperation() { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } @@ -338,7 +340,7 @@ static async IAsyncEnumerable GetNumbersAsync() [Fact] public async Task RegressionTest_ExceptionOnFirstMoveNextShouldNotFlushBuffer() { - if (StreamingSerializer is null) + if (StreamingSerializer?.IsAsyncSerializer != true) { return; } diff --git a/src/libraries/System.Text.Json/tests/Common/StreamingJsonSerializerWrapper.cs b/src/libraries/System.Text.Json/tests/Common/StreamingJsonSerializerWrapper.cs index d450ca05c75a7b..5b8f1c9ac3955c 100644 --- a/src/libraries/System.Text.Json/tests/Common/StreamingJsonSerializerWrapper.cs +++ b/src/libraries/System.Text.Json/tests/Common/StreamingJsonSerializerWrapper.cs @@ -15,7 +15,7 @@ public abstract partial class StreamingJsonSerializerWrapper : JsonSerializerWra /// /// True if the serializer is streaming data synchronously. /// - public virtual bool IsBlockingSerializer => false; + public abstract bool IsAsyncSerializer { get; } public abstract Task SerializeWrapper(Stream stream, object value, Type inputType, JsonSerializerOptions? options = null); public abstract Task SerializeWrapper(Stream stream, T value, JsonSerializerOptions? options = null); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.SourceGen.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.SourceGen.cs index f7bd07a453de00..9d02871c9dc620 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.SourceGen.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.SourceGen.cs @@ -84,6 +84,8 @@ internal sealed class AsyncStreamSerializerWrapper : StreamingJsonSerializerWrap private readonly JsonSerializerContext _defaultContext; private readonly Func _customContextCreator; + public override bool IsAsyncSerializer => true; + public AsyncStreamSerializerWrapper(JsonSerializerContext defaultContext!!, Func customContextCreator!!) { _defaultContext = defaultContext; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests.cs index 52b4fbdefca5fa..3b3d3ac1942d55 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CollectionTests.cs @@ -15,7 +15,11 @@ public sealed partial class CollectionTestsDynamic_AsyncStream : CollectionTests public CollectionTestsDynamic_AsyncStream() : base(JsonSerializerWrapper.AsyncStreamSerializer) { } } - [ActiveIssue("https://github.com/dotnet/runtime/issues/66687")] + public sealed partial class CollectionTestsDynamic_AsyncStreamWithSmallBuffer : CollectionTests + { + public CollectionTestsDynamic_AsyncStreamWithSmallBuffer() : base(JsonSerializerWrapper.AsyncStreamSerializerWithSmallBuffer) { } + } + public sealed partial class CollectionTestsDynamic_SyncStream : CollectionTests { public CollectionTestsDynamic_SyncStream() : base(JsonSerializerWrapper.SyncStreamSerializer) { } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapper.Reflection.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapper.Reflection.cs index f14e594d635134..2e3912a7572e98 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapper.Reflection.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapper.Reflection.cs @@ -122,6 +122,8 @@ private class AsyncStreamSerializerWrapper : StreamingJsonSerializerWrapper { private readonly bool _forceSmallBufferInOptions; + public override bool IsAsyncSerializer => true; + public AsyncStreamSerializerWrapper(bool forceSmallBufferInOptions = false) { _forceSmallBufferInOptions = forceSmallBufferInOptions; @@ -183,7 +185,7 @@ public SyncStreamSerializerWrapper(bool forceSmallBufferInOptions = false) private JsonSerializerOptions? ResolveOptionsInstance(JsonSerializerOptions? options) => _forceSmallBufferInOptions ? JsonSerializerOptionsSmallBufferMapper.ResolveOptionsInstanceWithSmallBuffer(options) : options; - public override bool IsBlockingSerializer => true; + public override bool IsAsyncSerializer => false; public override Task SerializeWrapper(Stream utf8Json, T value, JsonSerializerOptions options = null) {