Fix DeserializeAsyncEnumerable streaming behavior (#56080)
authorEirik Tsarpalis <eirik.tsarpalis@gmail.com>
Thu, 22 Jul 2021 20:29:22 +0000 (23:29 +0300)
committerGitHub <noreply@github.com>
Thu, 22 Jul 2021 20:29:22 +0000 (21:29 +0100)
Fixes regression introduced by #51702: the ContinueDeserialize method will return 'null' on partial reads so no elements will be yielded by the enumerator until the entire stream has been consumed.

This change reverts to the original implementation where the partially populated queue is being fetched from the ReadStack instead.

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs

index 6804387..d84191e 100644 (file)
@@ -388,8 +388,8 @@ namespace System.Text.Json
                     do
                     {
                         bufferState = await ReadFromStreamAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false);
-                        Queue<TValue>? queue = ContinueDeserialize<Queue<TValue>>(ref bufferState, ref jsonReaderState, ref readStack, converter, options);
-                        if (queue is not null)
+                        ContinueDeserialize<Queue<TValue>>(ref bufferState, ref jsonReaderState, ref readStack, converter, options);
+                        if (readStack.Current.ReturnValue is Queue<TValue> queue)
                         {
                             while (queue.Count > 0)
                             {
index 7d448fa..a058555 100644 (file)
@@ -75,6 +75,23 @@ namespace System.Text.Json.Serialization.Tests
         }
 
         [Fact]
+        public static async Task DeserializeAsyncEnumerable_ShouldStreamPartialData()
+        {
+            string json = JsonSerializer.Serialize(Enumerable.Range(0, 100));
+
+            using var stream = new Utf8MemoryStream(json);
+            IAsyncEnumerable<int> asyncEnumerable = JsonSerializer.DeserializeAsyncEnumerable<int>(stream, new JsonSerializerOptions { DefaultBufferSize = 1 });
+            await using IAsyncEnumerator<int> asyncEnumerator = asyncEnumerable.GetAsyncEnumerator();
+
+            for (int i = 0; i < 20; i++)
+            {
+                bool success = await asyncEnumerator.MoveNextAsync();
+                Assert.True(success, "AsyncEnumerator.MoveNextAsync() should return true.");
+                Assert.True(stream.Position < stream.Capacity / 2, "should have consumed less than half of the stream contents.");
+            }
+        }
+
+        [Fact]
         public static async Task DeserializeAsyncEnumerable_ShouldTolerateCustomQueueConverters()
         {
             const int expectedCount = 20;