< Summary

Line coverage
5%
Covered lines: 13
Uncovered lines: 206
Coverable lines: 219
Total lines: 381
Line coverage: 5.9%
Branch coverage
3%
Covered branches: 4
Total branches: 102
Branch coverage: 3.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Diagnostics.CodeAnalysis;
 6using System.Text.Json.Serialization.Metadata;
 7
 8namespace System.Text.Json.Serialization
 9{
 10    /// <summary>
 11    /// Base class for dictionary converters such as IDictionary, Hashtable, Dictionary{,} IDictionary{,} and SortedList
 12    /// </summary>
 13    internal abstract class JsonDictionaryConverter<TDictionary> : JsonResumableConverter<TDictionary>
 14    {
 15        internal override bool SupportsCreateObjectDelegate => true;
 16        private protected sealed override ConverterStrategy GetDefaultConverterStrategy() => ConverterStrategy.Dictionar
 17        internal sealed override JsonValueType GetSupportedJsonValueTypes(JsonNumberHandling _) => JsonValueType.Object;
 18
 19        protected internal abstract bool OnWriteResume(Utf8JsonWriter writer, TDictionary dictionary, JsonSerializerOpti
 20    }
 21
 22    /// <summary>
 23    /// Base class for dictionary converters such as IDictionary, Hashtable, Dictionary{,} IDictionary{,} and SortedList
 24    /// </summary>
 25    internal abstract class JsonDictionaryConverter<TDictionary, TKey, TValue> : JsonDictionaryConverter<TDictionary>
 26        where TKey : notnull
 27    {
 28        /// <summary>
 29        /// When overridden, adds the value to the collection.
 30        /// </summary>
 31        protected abstract void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state);
 32
 33        /// <summary>
 34        /// When overridden, converts the temporary collection held in state.Current.ReturnValue to the final collection
 35        /// This is used with immutable collections.
 36        /// </summary>
 037        protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }
 38
 39        /// <summary>
 40        /// When overridden, create the collection. It may be a temporary collection or the final collection.
 41        /// </summary>
 42        protected virtual void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state)
 043        {
 044            if (state.ParentProperty?.TryGetPrePopulatedValue(ref state) == true)
 045            {
 046                return;
 47            }
 48
 049            JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
 50
 051            if (typeInfo.CreateObject is null)
 052            {
 053                ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(typeInfo, ref reader, ref state);
 54            }
 55
 056            state.Current.ReturnValue = typeInfo.CreateObject();
 057            Debug.Assert(state.Current.ReturnValue is TDictionary);
 058        }
 59
 191960        internal override Type ElementType => typeof(TValue);
 61
 27162        internal override Type KeyType => typeof(TKey);
 63
 64
 65        protected JsonConverter<TKey>? _keyConverter;
 66        protected JsonConverter<TValue>? _valueConverter;
 67
 68        protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
 069        {
 070            return ((JsonTypeInfo<T>)typeInfo).EffectiveConverter;
 071        }
 72
 73        internal sealed override bool OnTryRead(
 74            ref Utf8JsonReader reader,
 75            Type typeToConvert,
 76            JsonSerializerOptions options,
 77            scoped ref ReadStack state,
 78            [MaybeNullWhen(false)] out TDictionary value)
 1279        {
 1280            JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;
 1281            JsonTypeInfo keyTypeInfo = jsonTypeInfo.KeyTypeInfo!;
 1282            JsonTypeInfo elementTypeInfo = jsonTypeInfo.ElementTypeInfo!;
 83
 1284            if (!state.SupportContinuation && !state.Current.CanContainMetadata)
 085            {
 86                // Fast path that avoids maintaining state variables and dealing with preserved references.
 87
 088                if (reader.TokenType != JsonTokenType.StartObject)
 089                {
 090                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 91                }
 92
 093                CreateCollection(ref reader, ref state);
 94
 095                jsonTypeInfo.OnDeserializing?.Invoke(state.Current.ReturnValue!);
 96
 097                _keyConverter ??= GetConverter<TKey>(keyTypeInfo);
 098                _valueConverter ??= GetConverter<TValue>(elementTypeInfo);
 99
 0100                if (_valueConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling is null)
 0101                {
 102                    // Process all elements.
 0103                    while (true)
 0104                    {
 105                        // Read the key name.
 0106                        reader.ReadWithVerify();
 107
 0108                        if (reader.TokenType == JsonTokenType.EndObject)
 0109                        {
 0110                            break;
 111                        }
 112
 113                        // Read method would have thrown if otherwise.
 0114                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
 115
 0116                        state.Current.JsonPropertyInfo = keyTypeInfo.PropertyInfoForTypeInfo;
 0117                        TKey key = ReadDictionaryKey(_keyConverter, ref reader, ref state, options);
 118
 119                        // Read the value and add.
 0120                        reader.ReadWithVerify();
 0121                        state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
 0122                        TValue? element = _valueConverter.Read(ref reader, ElementType, options);
 0123                        Add(key, element!, options, ref state);
 0124                    }
 0125                }
 126                else
 0127                {
 128                    // Process all elements.
 0129                    while (true)
 0130                    {
 131                        // Read the key name.
 0132                        reader.ReadWithVerify();
 133
 0134                        if (reader.TokenType == JsonTokenType.EndObject)
 0135                        {
 0136                            break;
 137                        }
 138
 139                        // Read method would have thrown if otherwise.
 0140                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
 0141                        state.Current.JsonPropertyInfo = keyTypeInfo.PropertyInfoForTypeInfo;
 0142                        TKey key = ReadDictionaryKey(_keyConverter, ref reader, ref state, options);
 143
 0144                        reader.ReadWithVerify();
 145
 146                        // Get the value from the converter and add it.
 0147                        state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
 0148                        _valueConverter.TryRead(ref reader, ElementType, options, ref state, out TValue? element, out _)
 0149                        Add(key, element!, options, ref state);
 0150                    }
 0151                }
 0152            }
 153            else
 12154            {
 155                // Slower path that supports continuation and reading metadata.
 12156                if (state.Current.ObjectState == StackFrameObjectState.None)
 12157                {
 12158                    if (reader.TokenType != JsonTokenType.StartObject)
 12159                    {
 12160                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 161                    }
 162
 0163                    state.Current.ObjectState = StackFrameObjectState.StartToken;
 0164                }
 165
 166                // Handle the metadata properties.
 0167                if (state.Current.CanContainMetadata && state.Current.ObjectState < StackFrameObjectState.ReadMetadata)
 0168                {
 0169                    if (!JsonSerializer.TryReadMetadata(this, jsonTypeInfo, ref reader, ref state))
 0170                    {
 0171                        value = default;
 0172                        return false;
 173                    }
 174
 0175                    if (state.Current.MetadataPropertyNames == MetadataPropertyName.Ref)
 0176                    {
 0177                        value = JsonSerializer.ResolveReferenceId<TDictionary>(ref state);
 0178                        return true;
 179                    }
 180
 0181                    state.Current.ObjectState = StackFrameObjectState.ReadMetadata;
 0182                }
 183
 184                // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress
 0185                if (((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 || state.PolymorphicResolved
 0186                    state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStart
 0187                    ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter)
 0188                {
 0189                    Debug.Assert(!IsValueType);
 0190                    bool success = polymorphicConverter.OnTryReadAsObject(ref reader, polymorphicConverter.Type!, option
 0191                    value = (TDictionary)objectResult!;
 0192                    state.ExitPolymorphicConverter(success);
 0193                    return success;
 194                }
 195
 196                // Create the dictionary.
 0197                if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
 0198                {
 0199                    if (state.Current.CanContainMetadata)
 0200                    {
 0201                        JsonSerializer.ValidateMetadataForObjectConverter(ref state);
 0202                    }
 203
 0204                    CreateCollection(ref reader, ref state);
 205
 0206                    if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0)
 0207                    {
 0208                        Debug.Assert(state.ReferenceId is not null);
 0209                        Debug.Assert(options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve);
 0210                        Debug.Assert(state.Current.ReturnValue is TDictionary);
 0211                        state.ReferenceResolver.AddReference(state.ReferenceId, state.Current.ReturnValue);
 0212                        state.ReferenceId = null;
 0213                    }
 214
 0215                    jsonTypeInfo.OnDeserializing?.Invoke(state.Current.ReturnValue!);
 216
 0217                    state.Current.ObjectState = StackFrameObjectState.CreatedObject;
 0218                }
 219
 220                // Process all elements.
 0221                _keyConverter ??= GetConverter<TKey>(keyTypeInfo);
 0222                _valueConverter ??= GetConverter<TValue>(elementTypeInfo);
 0223                while (true)
 0224                {
 0225                    if (state.Current.PropertyState == StackFramePropertyState.None)
 0226                    {
 227                        // Read the key name.
 0228                        if (!reader.Read())
 0229                        {
 0230                            value = default;
 0231                            return false;
 232                        }
 233
 0234                        state.Current.PropertyState = StackFramePropertyState.ReadName;
 0235                    }
 236
 237                    // Determine the property.
 238                    TKey key;
 0239                    if (state.Current.PropertyState < StackFramePropertyState.Name)
 0240                    {
 0241                        if (reader.TokenType == JsonTokenType.EndObject)
 0242                        {
 0243                            break;
 244                        }
 245
 246                        // Read method would have thrown if otherwise.
 0247                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
 248
 0249                        state.Current.PropertyState = StackFramePropertyState.Name;
 250
 0251                        if (state.Current.CanContainMetadata)
 0252                        {
 0253                            ReadOnlySpan<byte> propertyName = reader.GetUnescapedSpan();
 0254                            if (JsonSerializer.IsMetadataPropertyName(propertyName, state.Current.BaseJsonTypeInfo.Polym
 0255                            {
 0256                                if (options.AllowOutOfOrderMetadataProperties)
 0257                                {
 0258                                    reader.SkipWithVerify();
 0259                                    state.Current.EndElement();
 0260                                    continue;
 261                                }
 262                                else
 0263                                {
 0264                                    ThrowHelper.ThrowUnexpectedMetadataException(propertyName, ref reader, ref state);
 265                                }
 266                            }
 0267                        }
 268
 0269                        state.Current.JsonPropertyInfo = keyTypeInfo.PropertyInfoForTypeInfo;
 0270                        key = ReadDictionaryKey(_keyConverter, ref reader, ref state, options);
 0271                    }
 272                    else
 0273                    {
 274                        // DictionaryKey is assigned before all return false cases, null value is unreachable
 0275                        key = (TKey)state.Current.DictionaryKey!;
 0276                    }
 277
 0278                    if (state.Current.PropertyState < StackFramePropertyState.ReadValue)
 0279                    {
 0280                        if (!reader.TryAdvanceWithOptionalReadAhead(_valueConverter.RequiresReadAhead))
 0281                        {
 0282                            state.Current.DictionaryKey = key;
 0283                            value = default;
 0284                            return false;
 285                        }
 286
 0287                        state.Current.PropertyState = StackFramePropertyState.ReadValue;
 0288                    }
 289
 0290                    if (state.Current.PropertyState < StackFramePropertyState.TryRead)
 0291                    {
 292                        // Get the value from the converter and add it.
 0293                        state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
 0294                        bool success = _valueConverter.TryRead(ref reader, typeof(TValue), options, ref state, out TValu
 0295                        if (!success)
 0296                        {
 0297                            state.Current.DictionaryKey = key;
 0298                            value = default;
 0299                            return false;
 300                        }
 301
 0302                        Add(key, element!, options, ref state);
 0303                        state.Current.EndElement();
 0304                    }
 0305                }
 0306            }
 307
 0308            ConvertCollection(ref state, options);
 0309            object result = state.Current.ReturnValue!;
 0310            jsonTypeInfo.OnDeserialized?.Invoke(result);
 0311            value = (TDictionary)result;
 312
 0313            return true;
 314
 315            static TKey ReadDictionaryKey(JsonConverter<TKey> keyConverter, ref Utf8JsonReader reader, scoped ref ReadSt
 0316            {
 317                TKey key;
 0318                string unescapedPropertyNameAsString = reader.GetString()!;
 0319                state.Current.JsonPropertyNameAsString = unescapedPropertyNameAsString; // Copy key name for JSON Path s
 320
 321                // Special case string to avoid calling GetString twice and save one allocation.
 0322                if (keyConverter.IsInternalConverter && keyConverter.Type == typeof(string))
 0323                {
 0324                    key = (TKey)(object)unescapedPropertyNameAsString;
 0325                }
 326                else
 0327                {
 0328                    key = keyConverter.ReadAsPropertyNameCore(ref reader, keyConverter.Type, options);
 0329                }
 330
 0331                return key;
 0332            }
 0333        }
 334
 335        internal sealed override bool OnTryWrite(
 336            Utf8JsonWriter writer,
 337            TDictionary dictionary,
 338            JsonSerializerOptions options,
 339            ref WriteStack state)
 0340        {
 0341            if (dictionary is null)
 0342            {
 0343                writer.WriteNullValue();
 0344                return true;
 345            }
 346
 0347            JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;
 348
 0349            if (!state.Current.ProcessedStartToken)
 0350            {
 0351                state.Current.ProcessedStartToken = true;
 352
 0353                jsonTypeInfo.OnSerializing?.Invoke(dictionary);
 354
 0355                writer.WriteStartObject();
 356
 0357                if (state.CurrentContainsMetadata && CanHaveMetadata)
 0358                {
 0359                    JsonSerializer.WriteMetadataForObject(this, ref state, writer);
 0360                }
 361
 0362                state.Current.JsonPropertyInfo = jsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
 0363            }
 364
 0365            bool success = OnWriteResume(writer, dictionary, options, ref state);
 0366            if (success)
 0367            {
 0368                if (!state.Current.ProcessedEndToken)
 0369                {
 0370                    state.Current.ProcessedEndToken = true;
 0371                    writer.WriteEndObject();
 0372                }
 373
 0374                jsonTypeInfo.OnSerialized?.Invoke(dictionary);
 0375            }
 376
 0377            return success;
 0378        }
 379    }
 380}
 381