/// <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;
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);
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;
- }
}
}
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;
- }
}
}
// 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;
/// <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;
+ }
}
}
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)
}
}
- 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
/// 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)
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
- TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
+ TDictionary returnValue = (TDictionary)typeInfo.CreateObject()!;
if (returnValue.IsReadOnly)
{
}
}
- 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)
/// 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)
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(TypeToConvert, ref reader, ref state);
}
- TCollection returnValue = (TCollection)typeInfo.CreateObject()!;
+ TDictionary returnValue = (TDictionary)typeInfo.CreateObject()!;
if (returnValue.IsReadOnly)
{
}
}
- 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
/// </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)
// 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);
}
}
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>);
}
}
{
/// 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)
}
}
- 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
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)
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>);
}
}
}
}
- 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
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)
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;
- }
}
}
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;
- }
}
}
// 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);
}
}
// 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);
}
}
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;
- }
}
}
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;
- }
}
}
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)
{
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;
- }
}
}
{
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;
- }
}
}
{
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;
- }
}
}
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.