From: Eirik Tsarpalis Date: Thu, 5 Aug 2021 13:37:55 +0000 (+0300) Subject: Eliminate code duplication of OnWriteResume methods of collection converters (#55991) X-Git-Tag: accepted/tizen/unified/20220110.054933~627 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2fa3f974126592fae15707085d2749643d266886;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Eliminate code duplication of OnWriteResume methods of collection converters (#55991) * remove code duplication in collection converters * remove code duplication in dictionary converters --- diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs index 9147243..3ae0e3e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs @@ -9,9 +9,7 @@ namespace System.Text.Json.Serialization.Converters /// /// Converter for System.Array. /// - internal sealed class ArrayConverter - : IEnumerableDefaultConverter - where TCollection: IEnumerable + internal sealed class ArrayConverter : IEnumerableDefaultConverter { 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 elementConverter = GetElementConverter(ref state); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentQueueOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentQueueOfTConverter.cs index b456f9e..a795e8d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentQueueOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentQueueOfTConverter.cs @@ -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 enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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; - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentStackOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentStackOfTConverter.cs index d7588f8..f07be4a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentStackOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ConcurrentStackOfTConverter.cs @@ -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 enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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; - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs index 1c5147e..9f7fa9d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs @@ -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 /// /// Default base class implementation of JsonDictionaryConverter{TCollection} . /// - internal abstract class DictionaryDefaultConverter - : JsonDictionaryConverter + internal abstract class DictionaryDefaultConverter + : JsonDictionaryConverter + where TDictionary : IEnumerable> where TKey : notnull { - /// - /// When overridden, adds the value to the collection. - /// - protected abstract void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state); - - /// - /// When overridden, converts the temporary collection held in state.Current.ReturnValue to the final collection. - /// This is used with immutable collections. - /// - protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { } - - /// - /// When overridden, create the collection. It may be a temporary collection or the final collection. - /// - protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) { } - - internal override Type ElementType => typeof(TValue); - - internal override Type KeyType => typeof(TKey); - - - protected JsonConverter? _keyConverter; - protected JsonConverter? _valueConverter; - - protected static JsonConverter GetConverter(JsonTypeInfo typeInfo) - { - JsonConverter converter = (JsonConverter)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> 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(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(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(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 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>)state.Current.CollectionEnumerator; } - ConvertCollection(ref state, options); - value = (TCollection)state.Current.ReturnValue!; - return true; + JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; + _keyConverter ??= GetConverter(typeInfo.KeyTypeInfo!); + _valueConverter ??= GetConverter(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(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; + } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs index 3deef03..ca3def9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IAsyncEnumerableOfTConverter.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace System.Text.Json.Serialization.Converters { internal sealed class IAsyncEnumerableOfTConverter - : IEnumerableDefaultConverter + : JsonCollectionConverter where TAsyncEnumerable : IAsyncEnumerable { internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out TAsyncEnumerable value) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs index bc1e2fc..8d66c5e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs @@ -54,48 +54,6 @@ namespace System.Text.Json.Serialization.Converters } } - protected override bool OnWriteResume( - Utf8JsonWriter writer, - TCollection value, - JsonSerializerOptions options, - ref WriteStack state) - { - IEnumerator enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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 diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs index 710c5b1..bd605ad 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs @@ -11,13 +11,13 @@ namespace System.Text.Json.Serialization.Converters /// Converter for System.Collections.IDictionary that (de)serializes as a JSON object with properties /// representing the dictionary element key and value. /// - internal sealed class IDictionaryConverter - : DictionaryDefaultConverter - where TCollection : IDictionary + internal sealed class IDictionaryConverter + : JsonDictionaryConverter + 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) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs index 50e6335..7c5fb75 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs @@ -10,14 +10,14 @@ namespace System.Text.Json.Serialization.Converters /// Converter for System.Collections.Generic.IDictionary{TKey, TValue} that /// (de)serializes as a JSON object with properties representing the dictionary element key and value. /// - internal sealed class IDictionaryOfTKeyTValueConverter - : DictionaryDefaultConverter - where TCollection : IDictionary + internal sealed class IDictionaryOfTKeyTValueConverter + : DictionaryDefaultConverter + where TDictionary : IDictionary 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> enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator>)state.Current.CollectionEnumerator; - } - - JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; - _keyConverter ??= GetConverter(typeInfo.KeyTypeInfo!); - _valueConverter ??= GetConverter(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 diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs index ea7756a..368eabc 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverter.cs @@ -11,7 +11,7 @@ namespace System.Text.Json.Serialization.Converters /// /// internal sealed class IEnumerableConverter - : IEnumerableDefaultConverter + : JsonCollectionConverter where TCollection : IEnumerable { protected override void Add(in object? value, ref ReadStack state) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs index b1a0202..b623f42 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableDefaultConverter.cs @@ -1,283 +1,56 @@ // 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 { /// /// Default base class implementation of JsonIEnumerableConverter{TCollection, TElement}. /// - internal abstract class IEnumerableDefaultConverter - : JsonCollectionConverter + internal abstract class IEnumerableDefaultConverter : JsonCollectionConverter + where TCollection : IEnumerable { - 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 GetElementConverter(JsonTypeInfo elementTypeInfo) - { - JsonConverter converter = (JsonConverter)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 GetElementConverter(ref WriteStack state) - { - JsonConverter converter = (JsonConverter)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 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 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(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 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); + enumerator = (IEnumerator)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 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); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableOfTConverter.cs index 25eb1c9..ab2f48c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableOfTConverter.cs @@ -28,45 +28,6 @@ namespace System.Text.Json.Serialization.Converters state.Current.ReturnValue = new List(); } - protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) - { - IEnumerator enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - Debug.Assert(state.Current.CollectionEnumerator is IEnumerator); - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs index 2843450..e10e864 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs @@ -9,7 +9,7 @@ namespace System.Text.Json.Serialization.Converters { /// Converter for System.Collections.IList. internal sealed class IListConverter - : IEnumerableDefaultConverter + : JsonCollectionConverter where TCollection : IList { protected override void Add(in object? value, ref ReadStack state) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs index 2850c83..0695cfa 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs @@ -54,44 +54,6 @@ namespace System.Text.Json.Serialization.Converters } } - protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) - { - IEnumerator enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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 diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs index 8d161e3..11d953f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IReadOnlyDictionaryOfTKeyTValueConverter.cs @@ -6,9 +6,9 @@ using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { - internal sealed class IReadOnlyDictionaryOfTKeyTValueConverter - : DictionaryDefaultConverter - where TCollection : IReadOnlyDictionary + internal sealed class IReadOnlyDictionaryOfTKeyTValueConverter + : DictionaryDefaultConverter + where TDictionary : IReadOnlyDictionary 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(); } - protected internal override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) - { - IEnumerator> enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (Dictionary.Enumerator)state.Current.CollectionEnumerator; - } - - JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; - _keyConverter ??= GetConverter(typeInfo.KeyTypeInfo!); - _valueConverter ??= GetConverter(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); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs index b1486ae..9c6e2a1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs @@ -51,44 +51,6 @@ namespace System.Text.Json.Serialization.Converters } } - protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) - { - IEnumerator enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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 diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs index be6d5d1..78059a1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs @@ -7,9 +7,9 @@ using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { - internal class ImmutableDictionaryOfTKeyTValueConverter - : DictionaryDefaultConverter - where TCollection : IReadOnlyDictionary + internal class ImmutableDictionaryOfTKeyTValueConverter + : DictionaryDefaultConverter + where TDictionary : IReadOnlyDictionary 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>, TCollection>? creator = - (Func>, TCollection>?)state.Current.JsonTypeInfo.CreateObjectWithArgs; + Func>, TDictionary>? creator = + (Func>, TDictionary>?)state.Current.JsonTypeInfo.CreateObjectWithArgs; Debug.Assert(creator != null); state.Current.ReturnValue = creator((Dictionary)state.Current.ReturnValue!); } - - protected internal sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) - { - IEnumerator> enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator>)state.Current.CollectionEnumerator; - } - - JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; - _keyConverter ??= GetConverter(typeInfo.KeyTypeInfo!); - _valueConverter ??= GetConverter(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; - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs index f274a23..801b78b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs @@ -31,43 +31,5 @@ namespace System.Text.Json.Serialization.Converters Debug.Assert(creator != null); state.Current.ReturnValue = creator((List)state.Current.ReturnValue!); } - - protected sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) - { - IEnumerator enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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; - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs index c609535..517de63 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs @@ -1,14 +1,289 @@ // 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 { /// - /// Base class for all collections. Collections are assumed to implement System.Collections.IEnumerable. + /// Base class for all collections. Collections are assumed to implement + /// or a variant thereof e.g. . /// internal abstract class JsonCollectionConverter : JsonResumableConverter { 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 GetElementConverter(JsonTypeInfo elementTypeInfo) + { + JsonConverter converter = (JsonConverter)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 GetElementConverter(ref WriteStack state) + { + JsonConverter converter = (JsonConverter)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 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(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 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); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs index b6f0fc2..f26f713 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs @@ -1,15 +1,317 @@ // 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 { /// /// Base class for dictionary converters such as IDictionary, Hashtable, Dictionary{,} IDictionary{,} and SortedList. /// - internal abstract class JsonDictionaryConverter : JsonResumableConverter + internal abstract class JsonDictionaryConverter : JsonResumableConverter { 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); + } + + /// + /// Base class for dictionary converters such as IDictionary, Hashtable, Dictionary{,} IDictionary{,} and SortedList. + /// + internal abstract class JsonDictionaryConverter : JsonDictionaryConverter + where TKey : notnull + { + /// + /// When overridden, adds the value to the collection. + /// + protected abstract void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state); + + /// + /// When overridden, converts the temporary collection held in state.Current.ReturnValue to the final collection. + /// This is used with immutable collections. + /// + protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { } + + /// + /// When overridden, create the collection. It may be a temporary collection or the final collection. + /// + protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) { } + + internal override Type ElementType => typeof(TValue); + + internal override Type KeyType => typeof(TKey); + + + protected JsonConverter? _keyConverter; + protected JsonConverter? _valueConverter; + + protected static JsonConverter GetConverter(JsonTypeInfo typeInfo) + { + JsonConverter converter = (JsonConverter)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(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(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(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 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(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); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs index 84873d1..172a4cb5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs @@ -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 enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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; - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs index 7e1badf..565754d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs @@ -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 enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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; - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs index 04cc648..6bfc3e4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs @@ -8,7 +8,7 @@ using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { internal class StackOrQueueConverter - : IEnumerableDefaultConverter + : JsonCollectionConverter where TCollection : IEnumerable { protected sealed override void Add(in object? value, ref ReadStack state) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs index 036d04e..fe5249e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpListConverter.cs @@ -33,43 +33,5 @@ namespace System.Text.Json.Serialization.Converters { state.Current.ReturnValue = _listConstructor((List)state.Current.ReturnValue!); } - - protected override bool OnWriteResume(Utf8JsonWriter writer, TList value, JsonSerializerOptions options, ref WriteStack state) - { - IEnumerator enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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; - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs index 30d16a0..88d89f6 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpMapConverter.cs @@ -36,56 +36,5 @@ namespace System.Text.Json.Serialization.Converters { state.Current.ReturnValue = _mapConstructor((List>)state.Current.ReturnValue!); } - - protected internal override bool OnWriteResume(Utf8JsonWriter writer, TMap value, JsonSerializerOptions options, ref WriteStack state) - { - IEnumerator> enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator>)state.Current.CollectionEnumerator; - } - - JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; - _keyConverter ??= GetConverter(typeInfo.KeyTypeInfo!); - _valueConverter ??= GetConverter(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; - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs index 35c4646..0d41243 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/FSharp/FSharpSetConverter.cs @@ -33,43 +33,5 @@ namespace System.Text.Json.Serialization.Converters { state.Current.ReturnValue = _setConstructor((List)state.Current.ReturnValue!); } - - protected override bool OnWriteResume(Utf8JsonWriter writer, TSet value, JsonSerializerOptions options, ref WriteStack state) - { - IEnumerator enumerator; - if (state.Current.CollectionEnumerator == null) - { - enumerator = value.GetEnumerator(); - if (!enumerator.MoveNext()) - { - enumerator.Dispose(); - return true; - } - } - else - { - enumerator = (IEnumerator)state.Current.CollectionEnumerator; - } - - JsonConverter 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; - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 3a83812..564e63d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -475,7 +475,7 @@ namespace System.Text.Json.Serialization return TryWrite(writer, value, options, ref state); } - if (!(this is JsonDictionaryConverter dictionaryConverter)) + if (this is not JsonDictionaryConverter dictionaryConverter) { // If not JsonDictionaryConverter then we are JsonObject. // Avoid a type reference to JsonObject and its converter to support trimming.