Eliminate code duplication of OnWriteResume methods of collection converters (#55991)
authorEirik Tsarpalis <eirik.tsarpalis@gmail.com>
Thu, 5 Aug 2021 13:37:55 +0000 (16:37 +0300)
committerGitHub <noreply@github.com>
Thu, 5 Aug 2021 13:37:55 +0000 (14:37 +0100)
* remove code duplication in collection converters

* remove code duplication in dictionary converters

26 files changed:
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentQueueOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentStackOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs

index 9147243..3ae0e3e 100644 (file)
@@ -9,9 +9,7 @@ namespace System.Text.Json.Serialization.Converters
     /// <summary>
     /// Converter for <cref>System.Array</cref>.
     /// </summary>
-    internal sealed class ArrayConverter<TCollection, TElement>
-        : IEnumerableDefaultConverter<TCollection, TElement>
-        where TCollection: IEnumerable
+    internal sealed class ArrayConverter<TCollection, TElement> : IEnumerableDefaultConverter<TElement[], TElement>
     {
         internal override bool CanHaveIdMetadata => false;
 
@@ -31,10 +29,8 @@ namespace System.Text.Json.Serialization.Converters
             state.Current.ReturnValue = list.ToArray();
         }
 
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
+        protected override bool OnWriteResume(Utf8JsonWriter writer, TElement[] array, JsonSerializerOptions options, ref WriteStack state)
         {
-            TElement[] array = (TElement[])(IEnumerable)value;
-
             int index = state.Current.EnumeratorIndex;
 
             JsonConverter<TElement> elementConverter = GetElementConverter(ref state);
index b456f9e..a795e8d 100644 (file)
@@ -24,43 +24,5 @@ namespace System.Text.Json.Serialization.Converters
 
             state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
         }
-
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
     }
 }
index d7588f8..f07be4a 100644 (file)
@@ -24,43 +24,5 @@ namespace System.Text.Json.Serialization.Converters
 
             state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
         }
-
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
     }
 }
index 1c5147e..9f7fa9d 100644 (file)
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Text.Json.Serialization.Metadata;
@@ -10,299 +11,63 @@ namespace System.Text.Json.Serialization.Converters
     /// <summary>
     /// Default base class implementation of <cref>JsonDictionaryConverter{TCollection}</cref> .
     /// </summary>
-    internal abstract class DictionaryDefaultConverter<TCollection, TKey, TValue>
-        : JsonDictionaryConverter<TCollection>
+    internal abstract class DictionaryDefaultConverter<TDictionary, TKey, TValue>
+        : JsonDictionaryConverter<TDictionary, TKey, TValue>
+        where TDictionary : IEnumerable<KeyValuePair<TKey, TValue>>
         where TKey : notnull
     {
-        /// <summary>
-        /// When overridden, adds the value to the collection.
-        /// </summary>
-        protected abstract void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state);
-
-        /// <summary>
-        /// When overridden, converts the temporary collection held in state.Current.ReturnValue to the final collection.
-        /// This is used with immutable collections.
-        /// </summary>
-        protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }
-
-        /// <summary>
-        /// When overridden, create the collection. It may be a temporary collection or the final collection.
-        /// </summary>
-        protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) { }
-
-        internal override Type ElementType => typeof(TValue);
-
-        internal override Type KeyType => typeof(TKey);
-
-
-        protected JsonConverter<TKey>? _keyConverter;
-        protected JsonConverter<TValue>? _valueConverter;
-
-        protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
-        {
-            JsonConverter<T> converter = (JsonConverter<T>)typeInfo.PropertyInfoForTypeInfo.ConverterBase;
-            Debug.Assert(converter != null); // It should not be possible to have a null converter at this point.
-
-            return converter;
-        }
-
-        internal sealed override bool OnTryRead(
-            ref Utf8JsonReader reader,
-            Type typeToConvert,
+        protected internal override bool OnWriteResume(
+            Utf8JsonWriter writer,
+            TDictionary value,
             JsonSerializerOptions options,
-            ref ReadStack state,
-            [MaybeNullWhen(false)] out TCollection value)
+            ref WriteStack state)
         {
-            JsonTypeInfo elementTypeInfo = state.Current.JsonTypeInfo.ElementTypeInfo!;
-
-            if (state.UseFastPath)
+            IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
+            if (state.Current.CollectionEnumerator == null)
             {
-                // Fast path that avoids maintaining state variables and dealing with preserved references.
-
-                if (reader.TokenType != JsonTokenType.StartObject)
-                {
-                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
-                }
-
-                CreateCollection(ref reader, ref state);
-
-                _valueConverter ??= GetConverter<TValue>(elementTypeInfo);
-                if (_valueConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
+                enumerator = value.GetEnumerator();
+                if (!enumerator.MoveNext())
                 {
-                    // Process all elements.
-                    while (true)
-                    {
-                        // Read the key name.
-                        reader.ReadWithVerify();
-
-                        if (reader.TokenType == JsonTokenType.EndObject)
-                        {
-                            break;
-                        }
-
-                        // Read method would have thrown if otherwise.
-                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
-
-                        TKey key = ReadDictionaryKey(ref reader, ref state);
-
-                        // Read the value and add.
-                        reader.ReadWithVerify();
-                        TValue? element = _valueConverter.Read(ref reader, ElementType, options);
-                        Add(key, element!, options, ref state);
-                    }
-                }
-                else
-                {
-                    // Process all elements.
-                    while (true)
-                    {
-                        // Read the key name.
-                        reader.ReadWithVerify();
-
-                        if (reader.TokenType == JsonTokenType.EndObject)
-                        {
-                            break;
-                        }
-
-                        // Read method would have thrown if otherwise.
-                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
-
-                        TKey key = ReadDictionaryKey(ref reader, ref state);
-
-                        reader.ReadWithVerify();
-
-                        // Get the value from the converter and add it.
-                        _valueConverter.TryRead(ref reader, ElementType, options, ref state, out TValue? element);
-                        Add(key, element!, options, ref state);
-                    }
+                    enumerator.Dispose();
+                    return true;
                 }
             }
             else
             {
-                // Slower path that supports continuation and preserved references.
-
-                if (state.Current.ObjectState == StackFrameObjectState.None)
-                {
-                    if (reader.TokenType != JsonTokenType.StartObject)
-                    {
-                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
-                    }
-
-                    state.Current.ObjectState = StackFrameObjectState.StartToken;
-                }
-
-                // Handle the metadata properties.
-                bool preserveReferences = options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve;
-                if (preserveReferences && state.Current.ObjectState < StackFrameObjectState.PropertyValue)
-                {
-                    if (JsonSerializer.ResolveMetadataForJsonObject<TCollection>(ref reader, ref state, options))
-                    {
-                        if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
-                        {
-                            // This will never throw since it was previously validated in ResolveMetadataForJsonObject.
-                            value = (TCollection)state.Current.ReturnValue!;
-                            return true;
-                        }
-                    }
-                    else
-                    {
-                        value = default;
-                        return false;
-                    }
-                }
-
-                // Create the dictionary.
-                if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
-                {
-                    CreateCollection(ref reader, ref state);
-                    state.Current.ObjectState = StackFrameObjectState.CreatedObject;
-                }
-
-                // Process all elements.
-                _valueConverter ??= GetConverter<TValue>(elementTypeInfo);
-                while (true)
-                {
-                    if (state.Current.PropertyState == StackFramePropertyState.None)
-                    {
-                        state.Current.PropertyState = StackFramePropertyState.ReadName;
-
-                        // Read the key name.
-                        if (!reader.Read())
-                        {
-                            value = default;
-                            return false;
-                        }
-                    }
-
-                    // Determine the property.
-                    TKey key;
-                    if (state.Current.PropertyState < StackFramePropertyState.Name)
-                    {
-                        if (reader.TokenType == JsonTokenType.EndObject)
-                        {
-                            break;
-                        }
-
-                        // Read method would have thrown if otherwise.
-                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
-
-                        state.Current.PropertyState = StackFramePropertyState.Name;
-
-                        if (preserveReferences)
-                        {
-                            ReadOnlySpan<byte> propertyName = reader.GetSpan();
-                            if (propertyName.Length > 0 && propertyName[0] == '$')
-                            {
-                                ThrowHelper.ThrowUnexpectedMetadataException(propertyName, ref reader, ref state);
-                            }
-                        }
-
-                        key = ReadDictionaryKey(ref reader, ref state);
-                    }
-                    else
-                    {
-                        // DictionaryKey is assigned before all return false cases, null value is unreachable
-                        key = (TKey)state.Current.DictionaryKey!;
-                    }
-
-                    if (state.Current.PropertyState < StackFramePropertyState.ReadValue)
-                    {
-                        state.Current.PropertyState = StackFramePropertyState.ReadValue;
-
-                        if (!SingleValueReadWithReadAhead(_valueConverter.ConverterStrategy, ref reader, ref state))
-                        {
-                            state.Current.DictionaryKey = key;
-                            value = default;
-                            return false;
-                        }
-                    }
-
-                    if (state.Current.PropertyState < StackFramePropertyState.TryRead)
-                    {
-                        // Get the value from the converter and add it.
-                        bool success = _valueConverter.TryRead(ref reader, typeof(TValue), options, ref state, out TValue? element);
-                        if (!success)
-                        {
-                            state.Current.DictionaryKey = key;
-                            value = default;
-                            return false;
-                        }
-
-                        Add(key, element!, options, ref state);
-                        state.Current.EndElement();
-                    }
-                }
+                enumerator = (IEnumerator<KeyValuePair<TKey, TValue>>)state.Current.CollectionEnumerator;
             }
 
-            ConvertCollection(ref state, options);
-            value = (TCollection)state.Current.ReturnValue!;
-            return true;
+            JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
+            _keyConverter ??= GetConverter<TKey>(typeInfo.KeyTypeInfo!);
+            _valueConverter ??= GetConverter<TValue>(typeInfo.ElementTypeInfo!);
 
-            TKey ReadDictionaryKey(ref Utf8JsonReader reader, ref ReadStack state)
+            do
             {
-                TKey key;
-                string unescapedPropertyNameAsString;
-
-                // Special case string to avoid calling GetString twice and save one allocation.
-                if (KeyType == typeof(string))
-                {
-                    unescapedPropertyNameAsString = reader.GetString()!;
-                    key = (TKey)(object)unescapedPropertyNameAsString;
-                }
-                else
+                if (ShouldFlush(writer, ref state))
                 {
-                    _keyConverter ??= GetConverter<TKey>(state.Current.JsonTypeInfo.KeyTypeInfo!);
-                    key = _keyConverter.ReadWithQuotes(ref reader);
-                    unescapedPropertyNameAsString = reader.GetString()!;
+                    state.Current.CollectionEnumerator = enumerator;
+                    return false;
                 }
 
-                // Copy key name for JSON Path support in case of error.
-                state.Current.JsonPropertyNameAsString = unescapedPropertyNameAsString;
-                return key;
-            }
-        }
-
-        internal sealed override bool OnTryWrite(
-            Utf8JsonWriter writer,
-            TCollection dictionary,
-            JsonSerializerOptions options,
-            ref WriteStack state)
-        {
-            if (dictionary == null)
-            {
-                writer.WriteNullValue();
-                return true;
-            }
-
-            if (!state.Current.ProcessedStartToken)
-            {
-                state.Current.ProcessedStartToken = true;
-                writer.WriteStartObject();
-                if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve)
+                if (state.Current.PropertyState < StackFramePropertyState.Name)
                 {
-                    if (JsonSerializer.WriteReferenceForObject(this, dictionary, ref state, writer) == MetadataPropertyName.Ref)
-                    {
-                        return true;
-                    }
+                    state.Current.PropertyState = StackFramePropertyState.Name;
+                    TKey key = enumerator.Current.Key;
+                    _keyConverter.WriteWithQuotes(writer, key, options, ref state);
                 }
 
-                state.Current.DeclaredJsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
-            }
-
-            bool success = OnWriteResume(writer, dictionary, options, ref state);
-            if (success)
-            {
-                if (!state.Current.ProcessedEndToken)
+                TValue element = enumerator.Current.Value;
+                if (!_valueConverter.TryWrite(writer, element, options, ref state))
                 {
-                    state.Current.ProcessedEndToken = true;
-                    writer.WriteEndObject();
+                    state.Current.CollectionEnumerator = enumerator;
+                    return false;
                 }
-            }
 
-            return success;
-        }
+                state.Current.EndDictionaryElement();
+            } while (enumerator.MoveNext());
 
-        internal sealed override void CreateInstanceForReferenceResolver(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
-            => CreateCollection(ref reader, ref state);
+            enumerator.Dispose();
+            return true;
+        }
     }
 }
index 3deef03..ca3def9 100644 (file)
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
 namespace System.Text.Json.Serialization.Converters
 {
     internal sealed class IAsyncEnumerableOfTConverter<TAsyncEnumerable, TElement>
-        : IEnumerableDefaultConverter<TAsyncEnumerable, TElement>
+        : JsonCollectionConverter<TAsyncEnumerable, TElement>
         where TAsyncEnumerable : IAsyncEnumerable<TElement>
     {
         internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out TAsyncEnumerable value)
index bc1e2fc..8d66c5e 100644 (file)
@@ -54,48 +54,6 @@ namespace System.Text.Json.Serialization.Converters
             }
         }
 
-        protected override bool OnWriteResume(
-            Utf8JsonWriter writer,
-            TCollection value,
-            JsonSerializerOptions options,
-            ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
-
         internal override Type RuntimeType
         {
             get
index 710c5b1..bd605ad 100644 (file)
@@ -11,13 +11,13 @@ namespace System.Text.Json.Serialization.Converters
     /// Converter for <cref>System.Collections.IDictionary</cref> that (de)serializes as a JSON object with properties
     /// representing the dictionary element key and value.
     /// </summary>
-    internal sealed class IDictionaryConverter<TCollection>
-        : DictionaryDefaultConverter<TCollection, string, object?>
-        where TCollection : IDictionary
+    internal sealed class IDictionaryConverter<TDictionary>
+        : JsonDictionaryConverter<TDictionary, string, object?>
+        where TDictionary : IDictionary
     {
         protected override void Add(string key, in object? value, JsonSerializerOptions options, ref ReadStack state)
         {
-            TCollection collection = (TCollection)state.Current.ReturnValue!;
+            TDictionary collection = (TDictionary)state.Current.ReturnValue!;
             collection[key] = value;
             if (IsValueType)
             {
@@ -46,7 +46,7 @@ namespace System.Text.Json.Serialization.Converters
                     ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
                 }
 
-                TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
+                TDictionary returnValue = (TDictionary)typeInfo.CreateObject()!;
 
                 if (returnValue.IsReadOnly)
                 {
@@ -57,7 +57,7 @@ namespace System.Text.Json.Serialization.Converters
             }
         }
 
-        protected internal override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
+        protected internal override bool OnWriteResume(Utf8JsonWriter writer, TDictionary value, JsonSerializerOptions options, ref WriteStack state)
         {
             IDictionaryEnumerator enumerator;
             if (state.Current.CollectionEnumerator == null)
index 50e6335..7c5fb75 100644 (file)
@@ -10,14 +10,14 @@ namespace System.Text.Json.Serialization.Converters
     /// Converter for <cref>System.Collections.Generic.IDictionary{TKey, TValue}</cref> that
     /// (de)serializes as a JSON object with properties representing the dictionary element key and value.
     /// </summary>
-    internal sealed class IDictionaryOfTKeyTValueConverter<TCollection, TKey, TValue>
-        : DictionaryDefaultConverter<TCollection, TKey, TValue>
-        where TCollection : IDictionary<TKey, TValue>
+    internal sealed class IDictionaryOfTKeyTValueConverter<TDictionary, TKey, TValue>
+        : DictionaryDefaultConverter<TDictionary, TKey, TValue>
+        where TDictionary : IDictionary<TKey, TValue>
         where TKey : notnull
     {
         protected override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state)
         {
-            TCollection collection = (TCollection)state.Current.ReturnValue!;
+            TDictionary collection = (TDictionary)state.Current.ReturnValue!;
             collection[key] = value;
             if (IsValueType)
             {
@@ -45,7 +45,7 @@ namespace System.Text.Json.Serialization.Converters
                     ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
                 }
 
-                TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
+                TDictionary returnValue = (TDictionary)typeInfo.CreateObject()!;
 
                 if (returnValue.IsReadOnly)
                 {
@@ -56,60 +56,6 @@ namespace System.Text.Json.Serialization.Converters
             }
         }
 
-        protected internal override bool OnWriteResume(
-            Utf8JsonWriter writer,
-            TCollection value,
-            JsonSerializerOptions options,
-            ref WriteStack state)
-        {
-            IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<KeyValuePair<TKey, TValue>>)state.Current.CollectionEnumerator;
-            }
-
-            JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
-            _keyConverter ??= GetConverter<TKey>(typeInfo.KeyTypeInfo!);
-            _valueConverter ??= GetConverter<TValue>(typeInfo.ElementTypeInfo!);
-
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                if (state.Current.PropertyState < StackFramePropertyState.Name)
-                {
-                    state.Current.PropertyState = StackFramePropertyState.Name;
-                    TKey key = enumerator.Current.Key;
-                    _keyConverter.WriteWithQuotes(writer, key, options, ref state);
-                }
-
-                TValue element = enumerator.Current.Value;
-                if (!_valueConverter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                state.Current.EndDictionaryElement();
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
-
         internal override Type RuntimeType
         {
             get
index ea7756a..368eabc 100644 (file)
@@ -11,7 +11,7 @@ namespace System.Text.Json.Serialization.Converters
     /// </summary>
     /// <typeparam name="TCollection"></typeparam>
     internal sealed class IEnumerableConverter<TCollection>
-        : IEnumerableDefaultConverter<TCollection, object?>
+        : JsonCollectionConverter<TCollection, object?>
         where TCollection : IEnumerable
     {
         protected override void Add(in object? value, ref ReadStack state)
index b1a0202..b623f42 100644 (file)
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Collections.Generic;
 using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Text.Json.Serialization.Metadata;
 
 namespace System.Text.Json.Serialization.Converters
 {
     /// <summary>
     /// Default base class implementation of <cref>JsonIEnumerableConverter{TCollection, TElement}</cref>.
     /// </summary>
-    internal abstract class IEnumerableDefaultConverter<TCollection, TElement>
-        : JsonCollectionConverter<TCollection, TElement>
+    internal abstract class IEnumerableDefaultConverter<TCollection, TElement> : JsonCollectionConverter<TCollection, TElement>
+        where TCollection : IEnumerable<TElement>
     {
-        protected abstract void Add(in TElement value, ref ReadStack state);
-        protected abstract void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options);
-        protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }
-
-        protected static JsonConverter<TElement> GetElementConverter(JsonTypeInfo elementTypeInfo)
-        {
-            JsonConverter<TElement> converter = (JsonConverter<TElement>)elementTypeInfo.PropertyInfoForTypeInfo.ConverterBase;
-            Debug.Assert(converter != null); // It should not be possible to have a null converter at this point.
-
-            return converter;
-        }
-
-        protected static JsonConverter<TElement> GetElementConverter(ref WriteStack state)
-        {
-            JsonConverter<TElement> converter = (JsonConverter<TElement>)state.Current.DeclaredJsonPropertyInfo!.ConverterBase;
-            Debug.Assert(converter != null); // It should not be possible to have a null converter at this point.
-
-            return converter;
-        }
-
-        internal override bool OnTryRead(
-            ref Utf8JsonReader reader,
-            Type typeToConvert,
-            JsonSerializerOptions options,
-            ref ReadStack state,
-            [MaybeNullWhen(false)] out TCollection value)
+        protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
         {
-            JsonTypeInfo elementTypeInfo = state.Current.JsonTypeInfo.ElementTypeInfo!;
+            Debug.Assert(value is not null);
 
-            if (state.UseFastPath)
+            IEnumerator<TElement> enumerator;
+            if (state.Current.CollectionEnumerator == null)
             {
-                // Fast path that avoids maintaining state variables and dealing with preserved references.
-
-                if (reader.TokenType != JsonTokenType.StartArray)
-                {
-                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
-                }
-
-                CreateCollection(ref reader, ref state, options);
-
-                JsonConverter<TElement> elementConverter = GetElementConverter(elementTypeInfo);
-                if (elementConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
+                enumerator = value.GetEnumerator();
+                if (!enumerator.MoveNext())
                 {
-                    // Fast path that avoids validation and extra indirection.
-                    while (true)
-                    {
-                        reader.ReadWithVerify();
-                        if (reader.TokenType == JsonTokenType.EndArray)
-                        {
-                            break;
-                        }
-
-                        // Obtain the CLR value from the JSON and apply to the object.
-                        TElement? element = elementConverter.Read(ref reader, elementConverter.TypeToConvert, options);
-                        Add(element!, ref state);
-                    }
-                }
-                else
-                {
-                    // Process all elements.
-                    while (true)
-                    {
-                        reader.ReadWithVerify();
-                        if (reader.TokenType == JsonTokenType.EndArray)
-                        {
-                            break;
-                        }
-
-                        // Get the value from the converter and add it.
-                        elementConverter.TryRead(ref reader, typeof(TElement), options, ref state, out TElement? element);
-                        Add(element!, ref state);
-                    }
+                    enumerator.Dispose();
+                    return true;
                 }
             }
             else
             {
-                // Slower path that supports continuation and preserved references.
-
-                bool preserveReferences = options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve;
-                if (state.Current.ObjectState == StackFrameObjectState.None)
-                {
-                    if (reader.TokenType == JsonTokenType.StartArray)
-                    {
-                        state.Current.ObjectState = StackFrameObjectState.PropertyValue;
-                    }
-                    else if (preserveReferences)
-                    {
-                        if (reader.TokenType != JsonTokenType.StartObject)
-                        {
-                            ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
-                        }
-
-                        state.Current.ObjectState = StackFrameObjectState.StartToken;
-                    }
-                    else
-                    {
-                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
-                    }
-                }
-
-                // Handle the metadata properties.
-                if (preserveReferences && state.Current.ObjectState < StackFrameObjectState.PropertyValue)
-                {
-                    if (JsonSerializer.ResolveMetadataForJsonArray<TCollection>(ref reader, ref state, options))
-                    {
-                        if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
-                        {
-                            // This will never throw since it was previously validated in ResolveMetadataForJsonArray.
-                            value = (TCollection)state.Current.ReturnValue!;
-                            return true;
-                        }
-                    }
-                    else
-                    {
-                        value = default;
-                        return false;
-                    }
-                }
-
-                if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
-                {
-                    CreateCollection(ref reader, ref state, options);
-                    state.Current.JsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
-                    state.Current.ObjectState = StackFrameObjectState.CreatedObject;
-                }
-
-                if (state.Current.ObjectState < StackFrameObjectState.ReadElements)
-                {
-                    JsonConverter<TElement> elementConverter = GetElementConverter(elementTypeInfo);
-
-                    // Process all elements.
-                    while (true)
-                    {
-                        if (state.Current.PropertyState < StackFramePropertyState.ReadValue)
-                        {
-                            state.Current.PropertyState = StackFramePropertyState.ReadValue;
-
-                            if (!SingleValueReadWithReadAhead(elementConverter.ConverterStrategy, ref reader, ref state))
-                            {
-                                value = default;
-                                return false;
-                            }
-                        }
-
-                        if (state.Current.PropertyState < StackFramePropertyState.ReadValueIsEnd)
-                        {
-                            if (reader.TokenType == JsonTokenType.EndArray)
-                            {
-                                break;
-                            }
-
-                            state.Current.PropertyState = StackFramePropertyState.ReadValueIsEnd;
-                        }
-
-                        if (state.Current.PropertyState < StackFramePropertyState.TryRead)
-                        {
-                            // Get the value from the converter and add it.
-                            if (!elementConverter.TryRead(ref reader, typeof(TElement), options, ref state, out TElement? element))
-                            {
-                                value = default;
-                                return false;
-                            }
-
-                            Add(element!, ref state);
-
-                            // No need to set PropertyState to TryRead since we're done with this element now.
-                            state.Current.EndElement();
-                        }
-                    }
-
-                    state.Current.ObjectState = StackFrameObjectState.ReadElements;
-                }
-
-                if (state.Current.ObjectState < StackFrameObjectState.EndToken)
-                {
-                    state.Current.ObjectState = StackFrameObjectState.EndToken;
-
-                    // Read the EndObject token for an array with preserve semantics.
-                    if (state.Current.ValidateEndTokenOnArray)
-                    {
-                        if (!reader.Read())
-                        {
-                            value = default;
-                            return false;
-                        }
-                    }
-                }
-
-                if (state.Current.ObjectState < StackFrameObjectState.EndTokenValidation)
-                {
-                    if (state.Current.ValidateEndTokenOnArray)
-                    {
-                        if (reader.TokenType != JsonTokenType.EndObject)
-                        {
-                            Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
-                            ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(ref state, typeToConvert, reader);
-                        }
-                    }
-                }
+                Debug.Assert(state.Current.CollectionEnumerator is IEnumerator<TElement>);
+                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
             }
 
-            ConvertCollection(ref state, options);
-            value = (TCollection)state.Current.ReturnValue!;
-            return true;
-        }
-
-        internal override bool OnTryWrite(
-            Utf8JsonWriter writer,
-            TCollection value,
-            JsonSerializerOptions options,
-            ref WriteStack state)
-        {
-            bool success;
-
-            if (value == null)
-            {
-                writer.WriteNullValue();
-                success = true;
-            }
-            else
+            JsonConverter<TElement> converter = GetElementConverter(ref state);
+            do
             {
-                if (!state.Current.ProcessedStartToken)
+                if (ShouldFlush(writer, ref state))
                 {
-                    state.Current.ProcessedStartToken = true;
-                    if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve)
-                    {
-                        MetadataPropertyName metadata = JsonSerializer.WriteReferenceForCollection(this, value, ref state, writer);
-                        if (metadata == MetadataPropertyName.Ref)
-                        {
-                            return true;
-                        }
-
-                        state.Current.MetadataPropertyName = metadata;
-                    }
-                    else
-                    {
-                        writer.WriteStartArray();
-                    }
-
-                    state.Current.DeclaredJsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
+                    state.Current.CollectionEnumerator = enumerator;
+                    return false;
                 }
 
-                success = OnWriteResume(writer, value, options, ref state);
-                if (success)
+                TElement element = enumerator.Current;
+                if (!converter.TryWrite(writer, element, options, ref state))
                 {
-                    if (!state.Current.ProcessedEndToken)
-                    {
-                        state.Current.ProcessedEndToken = true;
-                        writer.WriteEndArray();
-
-                        if (state.Current.MetadataPropertyName == MetadataPropertyName.Id)
-                        {
-                            // Write the EndObject for $values.
-                            writer.WriteEndObject();
-                        }
-                    }
+                    state.Current.CollectionEnumerator = enumerator;
+                    return false;
                 }
-            }
+            } while (enumerator.MoveNext());
 
-            return success;
+            enumerator.Dispose();
+            return true;
         }
-
-        protected abstract bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state);
-
-        internal sealed override void CreateInstanceForReferenceResolver(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
-            => CreateCollection(ref reader, ref state, options);
     }
 }
index 25eb1c9..ab2f48c 100644 (file)
@@ -28,45 +28,6 @@ namespace System.Text.Json.Serialization.Converters
             state.Current.ReturnValue = new List<TElement>();
         }
 
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                Debug.Assert(state.Current.CollectionEnumerator is IEnumerator<TElement>);
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
-
         internal override Type RuntimeType => typeof(List<TElement>);
     }
 }
index 2843450..e10e864 100644 (file)
@@ -9,7 +9,7 @@ namespace System.Text.Json.Serialization.Converters
 {
     /// Converter for <cref>System.Collections.IList</cref>.
     internal sealed class IListConverter<TCollection>
-        : IEnumerableDefaultConverter<TCollection, object?>
+        : JsonCollectionConverter<TCollection, object?>
         where TCollection : IList
     {
         protected override void Add(in object? value, ref ReadStack state)
index 2850c83..0695cfa 100644 (file)
@@ -54,44 +54,6 @@ namespace System.Text.Json.Serialization.Converters
             }
         }
 
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
-
         internal override Type RuntimeType
         {
             get
index 8d161e3..11d953f 100644 (file)
@@ -6,9 +6,9 @@ using System.Text.Json.Serialization.Metadata;
 
 namespace System.Text.Json.Serialization.Converters
 {
-    internal sealed class IReadOnlyDictionaryOfTKeyTValueConverter<TCollection, TKey, TValue>
-        : DictionaryDefaultConverter<TCollection, TKey, TValue>
-        where TCollection : IReadOnlyDictionary<TKey, TValue>
+    internal sealed class IReadOnlyDictionaryOfTKeyTValueConverter<TDictionary, TKey, TValue>
+        : DictionaryDefaultConverter<TDictionary, TKey, TValue>
+        where TDictionary : IReadOnlyDictionary<TKey, TValue>
         where TKey : notnull
     {
         protected override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state)
@@ -26,57 +26,6 @@ namespace System.Text.Json.Serialization.Converters
             state.Current.ReturnValue = new Dictionary<TKey, TValue>();
         }
 
-        protected internal override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (Dictionary<TKey, TValue>.Enumerator)state.Current.CollectionEnumerator;
-            }
-
-            JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
-            _keyConverter ??= GetConverter<TKey>(typeInfo.KeyTypeInfo!);
-            _valueConverter ??= GetConverter<TValue>(typeInfo.ElementTypeInfo!);
-
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                if (state.Current.PropertyState < StackFramePropertyState.Name)
-                {
-                    state.Current.PropertyState = StackFramePropertyState.Name;
-
-                    TKey key = enumerator.Current.Key;
-                    _keyConverter.WriteWithQuotes(writer, key, options, ref state);
-                }
-
-                TValue element = enumerator.Current.Value;
-                if (!_valueConverter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                state.Current.EndDictionaryElement();
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
-
         internal override Type RuntimeType => typeof(Dictionary<TKey, TValue>);
     }
 }
index b1486ae..9c6e2a1 100644 (file)
@@ -51,44 +51,6 @@ namespace System.Text.Json.Serialization.Converters
             }
         }
 
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
-
         internal override Type RuntimeType
         {
             get
index be6d5d1..78059a1 100644 (file)
@@ -7,9 +7,9 @@ using System.Text.Json.Serialization.Metadata;
 
 namespace System.Text.Json.Serialization.Converters
 {
-    internal class ImmutableDictionaryOfTKeyTValueConverter<TCollection, TKey, TValue>
-        : DictionaryDefaultConverter<TCollection, TKey, TValue>
-        where TCollection : IReadOnlyDictionary<TKey, TValue>
+    internal class ImmutableDictionaryOfTKeyTValueConverter<TDictionary, TKey, TValue>
+        : DictionaryDefaultConverter<TDictionary, TKey, TValue>
+        where TDictionary : IReadOnlyDictionary<TKey, TValue>
         where TKey : notnull
     {
         protected sealed override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state)
@@ -26,61 +26,10 @@ namespace System.Text.Json.Serialization.Converters
 
         protected sealed override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options)
         {
-            Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>? creator =
-                (Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>?)state.Current.JsonTypeInfo.CreateObjectWithArgs;
+            Func<IEnumerable<KeyValuePair<TKey, TValue>>, TDictionary>? creator =
+                (Func<IEnumerable<KeyValuePair<TKey, TValue>>, TDictionary>?)state.Current.JsonTypeInfo.CreateObjectWithArgs;
             Debug.Assert(creator != null);
             state.Current.ReturnValue = creator((Dictionary<TKey, TValue>)state.Current.ReturnValue!);
         }
-
-        protected internal sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<KeyValuePair<TKey, TValue>>)state.Current.CollectionEnumerator;
-            }
-
-            JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
-            _keyConverter ??= GetConverter<TKey>(typeInfo.KeyTypeInfo!);
-            _valueConverter ??= GetConverter<TValue>(typeInfo.ElementTypeInfo!);
-
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                if (state.Current.PropertyState < StackFramePropertyState.Name)
-                {
-                    state.Current.PropertyState = StackFramePropertyState.Name;
-
-                    TKey key = enumerator.Current.Key;
-                    _keyConverter.WriteWithQuotes(writer, key, options, ref state);
-                }
-
-                TValue element = enumerator.Current.Value;
-                if (!_valueConverter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                state.Current.EndDictionaryElement();
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
     }
 }
index f274a23..801b78b 100644 (file)
@@ -31,43 +31,5 @@ namespace System.Text.Json.Serialization.Converters
             Debug.Assert(creator != null);
             state.Current.ReturnValue = creator((List<TElement>)state.Current.ReturnValue!);
         }
-
-        protected sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
     }
 }
index c609535..517de63 100644 (file)
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization.Metadata;
+
 namespace System.Text.Json.Serialization
 {
     /// <summary>
-    /// Base class for all collections. Collections are assumed to implement <cref>System.Collections.IEnumerable</cref>.
+    /// Base class for all collections. Collections are assumed to implement <see cref="IEnumerable{T}"/>
+    /// or a variant thereof e.g. <see cref="IAsyncEnumerable{T}"/>.
     /// </summary>
     internal abstract class JsonCollectionConverter<TCollection, TElement> : JsonResumableConverter<TCollection>
     {
         internal sealed override ConverterStrategy ConverterStrategy => ConverterStrategy.Enumerable;
         internal override Type ElementType => typeof(TElement);
+
+        protected abstract void Add(in TElement value, ref ReadStack state);
+        protected abstract void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options);
+        protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }
+
+        protected static JsonConverter<TElement> GetElementConverter(JsonTypeInfo elementTypeInfo)
+        {
+            JsonConverter<TElement> converter = (JsonConverter<TElement>)elementTypeInfo.PropertyInfoForTypeInfo.ConverterBase;
+            Debug.Assert(converter != null); // It should not be possible to have a null converter at this point.
+
+            return converter;
+        }
+
+        protected static JsonConverter<TElement> GetElementConverter(ref WriteStack state)
+        {
+            JsonConverter<TElement> converter = (JsonConverter<TElement>)state.Current.DeclaredJsonPropertyInfo!.ConverterBase;
+            Debug.Assert(converter != null); // It should not be possible to have a null converter at this point.
+
+            return converter;
+        }
+
+        internal override bool OnTryRead(
+            ref Utf8JsonReader reader,
+            Type typeToConvert,
+            JsonSerializerOptions options,
+            ref ReadStack state,
+            [MaybeNullWhen(false)] out TCollection value)
+        {
+            JsonTypeInfo elementTypeInfo = state.Current.JsonTypeInfo.ElementTypeInfo!;
+
+            if (state.UseFastPath)
+            {
+                // Fast path that avoids maintaining state variables and dealing with preserved references.
+
+                if (reader.TokenType != JsonTokenType.StartArray)
+                {
+                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
+                }
+
+                CreateCollection(ref reader, ref state, options);
+
+                JsonConverter<TElement> elementConverter = GetElementConverter(elementTypeInfo);
+                if (elementConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
+                {
+                    // Fast path that avoids validation and extra indirection.
+                    while (true)
+                    {
+                        reader.ReadWithVerify();
+                        if (reader.TokenType == JsonTokenType.EndArray)
+                        {
+                            break;
+                        }
+
+                        // Obtain the CLR value from the JSON and apply to the object.
+                        TElement? element = elementConverter.Read(ref reader, elementConverter.TypeToConvert, options);
+                        Add(element!, ref state);
+                    }
+                }
+                else
+                {
+                    // Process all elements.
+                    while (true)
+                    {
+                        reader.ReadWithVerify();
+                        if (reader.TokenType == JsonTokenType.EndArray)
+                        {
+                            break;
+                        }
+
+                        // Get the value from the converter and add it.
+                        elementConverter.TryRead(ref reader, typeof(TElement), options, ref state, out TElement? element);
+                        Add(element!, ref state);
+                    }
+                }
+            }
+            else
+            {
+                // Slower path that supports continuation and preserved references.
+
+                bool preserveReferences = options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve;
+                if (state.Current.ObjectState == StackFrameObjectState.None)
+                {
+                    if (reader.TokenType == JsonTokenType.StartArray)
+                    {
+                        state.Current.ObjectState = StackFrameObjectState.PropertyValue;
+                    }
+                    else if (preserveReferences)
+                    {
+                        if (reader.TokenType != JsonTokenType.StartObject)
+                        {
+                            ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
+                        }
+
+                        state.Current.ObjectState = StackFrameObjectState.StartToken;
+                    }
+                    else
+                    {
+                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
+                    }
+                }
+
+                // Handle the metadata properties.
+                if (preserveReferences && state.Current.ObjectState < StackFrameObjectState.PropertyValue)
+                {
+                    if (JsonSerializer.ResolveMetadataForJsonArray<TCollection>(ref reader, ref state, options))
+                    {
+                        if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
+                        {
+                            // This will never throw since it was previously validated in ResolveMetadataForJsonArray.
+                            value = (TCollection)state.Current.ReturnValue!;
+                            return true;
+                        }
+                    }
+                    else
+                    {
+                        value = default;
+                        return false;
+                    }
+                }
+
+                if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
+                {
+                    CreateCollection(ref reader, ref state, options);
+                    state.Current.JsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
+                    state.Current.ObjectState = StackFrameObjectState.CreatedObject;
+                }
+
+                if (state.Current.ObjectState < StackFrameObjectState.ReadElements)
+                {
+                    JsonConverter<TElement> elementConverter = GetElementConverter(elementTypeInfo);
+
+                    // Process all elements.
+                    while (true)
+                    {
+                        if (state.Current.PropertyState < StackFramePropertyState.ReadValue)
+                        {
+                            state.Current.PropertyState = StackFramePropertyState.ReadValue;
+
+                            if (!SingleValueReadWithReadAhead(elementConverter.ConverterStrategy, ref reader, ref state))
+                            {
+                                value = default;
+                                return false;
+                            }
+                        }
+
+                        if (state.Current.PropertyState < StackFramePropertyState.ReadValueIsEnd)
+                        {
+                            if (reader.TokenType == JsonTokenType.EndArray)
+                            {
+                                break;
+                            }
+
+                            state.Current.PropertyState = StackFramePropertyState.ReadValueIsEnd;
+                        }
+
+                        if (state.Current.PropertyState < StackFramePropertyState.TryRead)
+                        {
+                            // Get the value from the converter and add it.
+                            if (!elementConverter.TryRead(ref reader, typeof(TElement), options, ref state, out TElement? element))
+                            {
+                                value = default;
+                                return false;
+                            }
+
+                            Add(element!, ref state);
+
+                            // No need to set PropertyState to TryRead since we're done with this element now.
+                            state.Current.EndElement();
+                        }
+                    }
+
+                    state.Current.ObjectState = StackFrameObjectState.ReadElements;
+                }
+
+                if (state.Current.ObjectState < StackFrameObjectState.EndToken)
+                {
+                    state.Current.ObjectState = StackFrameObjectState.EndToken;
+
+                    // Read the EndObject token for an array with preserve semantics.
+                    if (state.Current.ValidateEndTokenOnArray)
+                    {
+                        if (!reader.Read())
+                        {
+                            value = default;
+                            return false;
+                        }
+                    }
+                }
+
+                if (state.Current.ObjectState < StackFrameObjectState.EndTokenValidation)
+                {
+                    if (state.Current.ValidateEndTokenOnArray)
+                    {
+                        if (reader.TokenType != JsonTokenType.EndObject)
+                        {
+                            Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
+                            ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(ref state, typeToConvert, reader);
+                        }
+                    }
+                }
+            }
+
+            ConvertCollection(ref state, options);
+            value = (TCollection)state.Current.ReturnValue!;
+            return true;
+        }
+
+        internal override bool OnTryWrite(
+            Utf8JsonWriter writer,
+            TCollection value,
+            JsonSerializerOptions options,
+            ref WriteStack state)
+        {
+            bool success;
+
+            if (value == null)
+            {
+                writer.WriteNullValue();
+                success = true;
+            }
+            else
+            {
+                if (!state.Current.ProcessedStartToken)
+                {
+                    state.Current.ProcessedStartToken = true;
+                    if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve)
+                    {
+                        MetadataPropertyName metadata = JsonSerializer.WriteReferenceForCollection(this, value, ref state, writer);
+                        if (metadata == MetadataPropertyName.Ref)
+                        {
+                            return true;
+                        }
+
+                        state.Current.MetadataPropertyName = metadata;
+                    }
+                    else
+                    {
+                        writer.WriteStartArray();
+                    }
+
+                    state.Current.DeclaredJsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
+                }
+
+                success = OnWriteResume(writer, value, options, ref state);
+                if (success)
+                {
+                    if (!state.Current.ProcessedEndToken)
+                    {
+                        state.Current.ProcessedEndToken = true;
+                        writer.WriteEndArray();
+
+                        if (state.Current.MetadataPropertyName == MetadataPropertyName.Id)
+                        {
+                            // Write the EndObject for $values.
+                            writer.WriteEndObject();
+                        }
+                    }
+                }
+            }
+
+            return success;
+        }
+
+        protected abstract bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state);
+
+        internal sealed override void CreateInstanceForReferenceResolver(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
+            => CreateCollection(ref reader, ref state, options);
     }
 }
index b6f0fc2..f26f713 100644 (file)
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization.Metadata;
+
 namespace System.Text.Json.Serialization
 {
     /// <summary>
     /// Base class for dictionary converters such as IDictionary, Hashtable, Dictionary{,} IDictionary{,} and SortedList.
     /// </summary>
-    internal abstract class JsonDictionaryConverter<T> : JsonResumableConverter<T>
+    internal abstract class JsonDictionaryConverter<TDictionary> : JsonResumableConverter<TDictionary>
     {
         internal sealed override ConverterStrategy ConverterStrategy => ConverterStrategy.Dictionary;
 
-        protected internal abstract bool OnWriteResume(Utf8JsonWriter writer, T dictionary, JsonSerializerOptions options, ref WriteStack state);
+        protected internal abstract bool OnWriteResume(Utf8JsonWriter writer, TDictionary dictionary, JsonSerializerOptions options, ref WriteStack state);
+    }
+
+    /// <summary>
+    /// Base class for dictionary converters such as IDictionary, Hashtable, Dictionary{,} IDictionary{,} and SortedList.
+    /// </summary>
+    internal abstract class JsonDictionaryConverter<TDictionary, TKey, TValue> : JsonDictionaryConverter<TDictionary>
+        where TKey : notnull
+    {
+        /// <summary>
+        /// When overridden, adds the value to the collection.
+        /// </summary>
+        protected abstract void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state);
+
+        /// <summary>
+        /// When overridden, converts the temporary collection held in state.Current.ReturnValue to the final collection.
+        /// This is used with immutable collections.
+        /// </summary>
+        protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }
+
+        /// <summary>
+        /// When overridden, create the collection. It may be a temporary collection or the final collection.
+        /// </summary>
+        protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) { }
+
+        internal override Type ElementType => typeof(TValue);
+
+        internal override Type KeyType => typeof(TKey);
+
+
+        protected JsonConverter<TKey>? _keyConverter;
+        protected JsonConverter<TValue>? _valueConverter;
+
+        protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
+        {
+            JsonConverter<T> converter = (JsonConverter<T>)typeInfo.PropertyInfoForTypeInfo.ConverterBase;
+            Debug.Assert(converter != null); // It should not be possible to have a null converter at this point.
+
+            return converter;
+        }
+
+        internal sealed override bool OnTryRead(
+            ref Utf8JsonReader reader,
+            Type typeToConvert,
+            JsonSerializerOptions options,
+            ref ReadStack state,
+            [MaybeNullWhen(false)] out TDictionary value)
+        {
+            JsonTypeInfo elementTypeInfo = state.Current.JsonTypeInfo.ElementTypeInfo!;
+
+            if (state.UseFastPath)
+            {
+                // Fast path that avoids maintaining state variables and dealing with preserved references.
+
+                if (reader.TokenType != JsonTokenType.StartObject)
+                {
+                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
+                }
+
+                CreateCollection(ref reader, ref state);
+
+                _valueConverter ??= GetConverter<TValue>(elementTypeInfo);
+                if (_valueConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
+                {
+                    // Process all elements.
+                    while (true)
+                    {
+                        // Read the key name.
+                        reader.ReadWithVerify();
+
+                        if (reader.TokenType == JsonTokenType.EndObject)
+                        {
+                            break;
+                        }
+
+                        // Read method would have thrown if otherwise.
+                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
+
+                        TKey key = ReadDictionaryKey(ref reader, ref state);
+
+                        // Read the value and add.
+                        reader.ReadWithVerify();
+                        TValue? element = _valueConverter.Read(ref reader, ElementType, options);
+                        Add(key, element!, options, ref state);
+                    }
+                }
+                else
+                {
+                    // Process all elements.
+                    while (true)
+                    {
+                        // Read the key name.
+                        reader.ReadWithVerify();
+
+                        if (reader.TokenType == JsonTokenType.EndObject)
+                        {
+                            break;
+                        }
+
+                        // Read method would have thrown if otherwise.
+                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
+
+                        TKey key = ReadDictionaryKey(ref reader, ref state);
+
+                        reader.ReadWithVerify();
+
+                        // Get the value from the converter and add it.
+                        _valueConverter.TryRead(ref reader, ElementType, options, ref state, out TValue? element);
+                        Add(key, element!, options, ref state);
+                    }
+                }
+            }
+            else
+            {
+                // Slower path that supports continuation and preserved references.
+
+                if (state.Current.ObjectState == StackFrameObjectState.None)
+                {
+                    if (reader.TokenType != JsonTokenType.StartObject)
+                    {
+                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert);
+                    }
+
+                    state.Current.ObjectState = StackFrameObjectState.StartToken;
+                }
+
+                // Handle the metadata properties.
+                bool preserveReferences = options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve;
+                if (preserveReferences && state.Current.ObjectState < StackFrameObjectState.PropertyValue)
+                {
+                    if (JsonSerializer.ResolveMetadataForJsonObject<TDictionary>(ref reader, ref state, options))
+                    {
+                        if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
+                        {
+                            // This will never throw since it was previously validated in ResolveMetadataForJsonObject.
+                            value = (TDictionary)state.Current.ReturnValue!;
+                            return true;
+                        }
+                    }
+                    else
+                    {
+                        value = default;
+                        return false;
+                    }
+                }
+
+                // Create the dictionary.
+                if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
+                {
+                    CreateCollection(ref reader, ref state);
+                    state.Current.ObjectState = StackFrameObjectState.CreatedObject;
+                }
+
+                // Process all elements.
+                _valueConverter ??= GetConverter<TValue>(elementTypeInfo);
+                while (true)
+                {
+                    if (state.Current.PropertyState == StackFramePropertyState.None)
+                    {
+                        state.Current.PropertyState = StackFramePropertyState.ReadName;
+
+                        // Read the key name.
+                        if (!reader.Read())
+                        {
+                            value = default;
+                            return false;
+                        }
+                    }
+
+                    // Determine the property.
+                    TKey key;
+                    if (state.Current.PropertyState < StackFramePropertyState.Name)
+                    {
+                        if (reader.TokenType == JsonTokenType.EndObject)
+                        {
+                            break;
+                        }
+
+                        // Read method would have thrown if otherwise.
+                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
+
+                        state.Current.PropertyState = StackFramePropertyState.Name;
+
+                        if (preserveReferences)
+                        {
+                            ReadOnlySpan<byte> propertyName = reader.GetSpan();
+                            if (propertyName.Length > 0 && propertyName[0] == '$')
+                            {
+                                ThrowHelper.ThrowUnexpectedMetadataException(propertyName, ref reader, ref state);
+                            }
+                        }
+
+                        key = ReadDictionaryKey(ref reader, ref state);
+                    }
+                    else
+                    {
+                        // DictionaryKey is assigned before all return false cases, null value is unreachable
+                        key = (TKey)state.Current.DictionaryKey!;
+                    }
+
+                    if (state.Current.PropertyState < StackFramePropertyState.ReadValue)
+                    {
+                        state.Current.PropertyState = StackFramePropertyState.ReadValue;
+
+                        if (!SingleValueReadWithReadAhead(_valueConverter.ConverterStrategy, ref reader, ref state))
+                        {
+                            state.Current.DictionaryKey = key;
+                            value = default;
+                            return false;
+                        }
+                    }
+
+                    if (state.Current.PropertyState < StackFramePropertyState.TryRead)
+                    {
+                        // Get the value from the converter and add it.
+                        bool success = _valueConverter.TryRead(ref reader, typeof(TValue), options, ref state, out TValue? element);
+                        if (!success)
+                        {
+                            state.Current.DictionaryKey = key;
+                            value = default;
+                            return false;
+                        }
+
+                        Add(key, element!, options, ref state);
+                        state.Current.EndElement();
+                    }
+                }
+            }
+
+            ConvertCollection(ref state, options);
+            value = (TDictionary)state.Current.ReturnValue!;
+            return true;
+
+            TKey ReadDictionaryKey(ref Utf8JsonReader reader, ref ReadStack state)
+            {
+                TKey key;
+                string unescapedPropertyNameAsString;
+
+                // Special case string to avoid calling GetString twice and save one allocation.
+                if (KeyType == typeof(string))
+                {
+                    unescapedPropertyNameAsString = reader.GetString()!;
+                    key = (TKey)(object)unescapedPropertyNameAsString;
+                }
+                else
+                {
+                    _keyConverter ??= GetConverter<TKey>(state.Current.JsonTypeInfo.KeyTypeInfo!);
+                    key = _keyConverter.ReadWithQuotes(ref reader);
+                    unescapedPropertyNameAsString = reader.GetString()!;
+                }
+
+                // Copy key name for JSON Path support in case of error.
+                state.Current.JsonPropertyNameAsString = unescapedPropertyNameAsString;
+                return key;
+            }
+        }
+
+        internal sealed override bool OnTryWrite(
+            Utf8JsonWriter writer,
+            TDictionary dictionary,
+            JsonSerializerOptions options,
+            ref WriteStack state)
+        {
+            if (dictionary == null)
+            {
+                writer.WriteNullValue();
+                return true;
+            }
+
+            if (!state.Current.ProcessedStartToken)
+            {
+                state.Current.ProcessedStartToken = true;
+                writer.WriteStartObject();
+                if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve)
+                {
+                    if (JsonSerializer.WriteReferenceForObject(this, dictionary, ref state, writer) == MetadataPropertyName.Ref)
+                    {
+                        return true;
+                    }
+                }
+
+                state.Current.DeclaredJsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
+            }
+
+            bool success = OnWriteResume(writer, dictionary, options, ref state);
+            if (success)
+            {
+                if (!state.Current.ProcessedEndToken)
+                {
+                    state.Current.ProcessedEndToken = true;
+                    writer.WriteEndObject();
+                }
+            }
+
+            return success;
+        }
+
+        internal sealed override void CreateInstanceForReferenceResolver(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
+            => CreateCollection(ref reader, ref state);
     }
 }
index 84873d1..172a4cb 100644 (file)
@@ -27,43 +27,5 @@ namespace System.Text.Json.Serialization.Converters
 
             state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
         }
-
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
     }
 }
index 7e1badf..565754d 100644 (file)
@@ -24,43 +24,5 @@ namespace System.Text.Json.Serialization.Converters
 
             state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject();
         }
-
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
     }
 }
index 04cc648..6bfc3e4 100644 (file)
@@ -8,7 +8,7 @@ using System.Text.Json.Serialization.Metadata;
 namespace System.Text.Json.Serialization.Converters
 {
     internal class StackOrQueueConverter<TCollection>
-        : IEnumerableDefaultConverter<TCollection, object?>
+        : JsonCollectionConverter<TCollection, object?>
         where TCollection : IEnumerable
     {
         protected sealed override void Add(in object? value, ref ReadStack state)
index 036d04e..fe5249e 100644 (file)
@@ -33,43 +33,5 @@ namespace System.Text.Json.Serialization.Converters
         {
             state.Current.ReturnValue = _listConstructor((List<TElement>)state.Current.ReturnValue!);
         }
-
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TList value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
     }
 }
index 30d16a0..88d89f6 100644 (file)
@@ -36,56 +36,5 @@ namespace System.Text.Json.Serialization.Converters
         {
             state.Current.ReturnValue = _mapConstructor((List<Tuple<TKey, TValue>>)state.Current.ReturnValue!);
         }
-
-        protected internal override bool OnWriteResume(Utf8JsonWriter writer, TMap value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<KeyValuePair<TKey, TValue>>)state.Current.CollectionEnumerator;
-            }
-
-            JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
-            _keyConverter ??= GetConverter<TKey>(typeInfo.KeyTypeInfo!);
-            _valueConverter ??= GetConverter<TValue>(typeInfo.ElementTypeInfo!);
-
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                if (state.Current.PropertyState < StackFramePropertyState.Name)
-                {
-                    state.Current.PropertyState = StackFramePropertyState.Name;
-
-                    TKey key = enumerator.Current.Key;
-                    _keyConverter.WriteWithQuotes(writer, key, options, ref state);
-                }
-
-                TValue element = enumerator.Current.Value;
-                if (!_valueConverter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                state.Current.EndDictionaryElement();
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
     }
 }
index 35c4646..0d41243 100644 (file)
@@ -33,43 +33,5 @@ namespace System.Text.Json.Serialization.Converters
         {
             state.Current.ReturnValue = _setConstructor((List<TElement>)state.Current.ReturnValue!);
         }
-
-        protected override bool OnWriteResume(Utf8JsonWriter writer, TSet value, JsonSerializerOptions options, ref WriteStack state)
-        {
-            IEnumerator<TElement> enumerator;
-            if (state.Current.CollectionEnumerator == null)
-            {
-                enumerator = value.GetEnumerator();
-                if (!enumerator.MoveNext())
-                {
-                    enumerator.Dispose();
-                    return true;
-                }
-            }
-            else
-            {
-                enumerator = (IEnumerator<TElement>)state.Current.CollectionEnumerator;
-            }
-
-            JsonConverter<TElement> converter = GetElementConverter(ref state);
-            do
-            {
-                if (ShouldFlush(writer, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-
-                TElement element = enumerator.Current;
-                if (!converter.TryWrite(writer, element, options, ref state))
-                {
-                    state.Current.CollectionEnumerator = enumerator;
-                    return false;
-                }
-            } while (enumerator.MoveNext());
-
-            enumerator.Dispose();
-            return true;
-        }
     }
 }
index 3a83812..564e63d 100644 (file)
@@ -475,7 +475,7 @@ namespace System.Text.Json.Serialization
                 return TryWrite(writer, value, options, ref state);
             }
 
-            if (!(this is JsonDictionaryConverter<T> dictionaryConverter))
+            if (this is not JsonDictionaryConverter<T> dictionaryConverter)
             {
                 // If not JsonDictionaryConverter<T> then we are JsonObject.
                 // Avoid a type reference to JsonObject and its converter to support trimming.