< Summary

Information
Line coverage
100%
Covered lines: 2
Uncovered lines: 0
Coverable lines: 2
Total lines: 379
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
GetDefaultConverterStrategy()100%11100%

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\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    {
 72815        internal override bool SupportsCreateObjectDelegate => true;
 36416        private protected sealed override ConverterStrategy GetDefaultConverterStrategy() => ConverterStrategy.Dictionar
 17
 18        protected internal abstract bool OnWriteResume(Utf8JsonWriter writer, TDictionary dictionary, JsonSerializerOpti
 19    }
 20
 21    /// <summary>
 22    /// Base class for dictionary converters such as IDictionary, Hashtable, Dictionary{,} IDictionary{,} and SortedList
 23    /// </summary>
 24    internal abstract class JsonDictionaryConverter<TDictionary, TKey, TValue> : JsonDictionaryConverter<TDictionary>
 25        where TKey : notnull
 26    {
 27        /// <summary>
 28        /// When overridden, adds the value to the collection.
 29        /// </summary>
 30        protected abstract void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state);
 31
 32        /// <summary>
 33        /// When overridden, converts the temporary collection held in state.Current.ReturnValue to the final collection
 34        /// This is used with immutable collections.
 35        /// </summary>
 36        protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }
 37
 38        /// <summary>
 39        /// When overridden, create the collection. It may be a temporary collection or the final collection.
 40        /// </summary>
 41        protected virtual void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state)
 42        {
 43            if (state.ParentProperty?.TryGetPrePopulatedValue(ref state) == true)
 44            {
 45                return;
 46            }
 47
 48            JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
 49
 50            if (typeInfo.CreateObject is null)
 51            {
 52                ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(typeInfo, ref reader, ref state);
 53            }
 54
 55            state.Current.ReturnValue = typeInfo.CreateObject();
 56            Debug.Assert(state.Current.ReturnValue is TDictionary);
 57        }
 58
 59        internal override Type ElementType => typeof(TValue);
 60
 61        internal override Type KeyType => typeof(TKey);
 62
 63
 64        protected JsonConverter<TKey>? _keyConverter;
 65        protected JsonConverter<TValue>? _valueConverter;
 66
 67        protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
 68        {
 69            return ((JsonTypeInfo<T>)typeInfo).EffectiveConverter;
 70        }
 71
 72        internal sealed override bool OnTryRead(
 73            ref Utf8JsonReader reader,
 74            Type typeToConvert,
 75            JsonSerializerOptions options,
 76            scoped ref ReadStack state,
 77            [MaybeNullWhen(false)] out TDictionary value)
 78        {
 79            JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;
 80            JsonTypeInfo keyTypeInfo = jsonTypeInfo.KeyTypeInfo!;
 81            JsonTypeInfo elementTypeInfo = jsonTypeInfo.ElementTypeInfo!;
 82
 83            if (!state.SupportContinuation && !state.Current.CanContainMetadata)
 84            {
 85                // Fast path that avoids maintaining state variables and dealing with preserved references.
 86
 87                if (reader.TokenType != JsonTokenType.StartObject)
 88                {
 89                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 90                }
 91
 92                CreateCollection(ref reader, ref state);
 93
 94                jsonTypeInfo.OnDeserializing?.Invoke(state.Current.ReturnValue!);
 95
 96                _keyConverter ??= GetConverter<TKey>(keyTypeInfo);
 97                _valueConverter ??= GetConverter<TValue>(elementTypeInfo);
 98
 99                if (_valueConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
 100                {
 101                    // Process all elements.
 102                    while (true)
 103                    {
 104                        // Read the key name.
 105                        reader.ReadWithVerify();
 106
 107                        if (reader.TokenType == JsonTokenType.EndObject)
 108                        {
 109                            break;
 110                        }
 111
 112                        // Read method would have thrown if otherwise.
 113                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
 114
 115                        state.Current.JsonPropertyInfo = keyTypeInfo.PropertyInfoForTypeInfo;
 116                        TKey key = ReadDictionaryKey(_keyConverter, ref reader, ref state, options);
 117
 118                        // Read the value and add.
 119                        reader.ReadWithVerify();
 120                        state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
 121                        TValue? element = _valueConverter.Read(ref reader, ElementType, options);
 122                        Add(key, element!, options, ref state);
 123                    }
 124                }
 125                else
 126                {
 127                    // Process all elements.
 128                    while (true)
 129                    {
 130                        // Read the key name.
 131                        reader.ReadWithVerify();
 132
 133                        if (reader.TokenType == JsonTokenType.EndObject)
 134                        {
 135                            break;
 136                        }
 137
 138                        // Read method would have thrown if otherwise.
 139                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
 140                        state.Current.JsonPropertyInfo = keyTypeInfo.PropertyInfoForTypeInfo;
 141                        TKey key = ReadDictionaryKey(_keyConverter, ref reader, ref state, options);
 142
 143                        reader.ReadWithVerify();
 144
 145                        // Get the value from the converter and add it.
 146                        state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
 147                        _valueConverter.TryRead(ref reader, ElementType, options, ref state, out TValue? element, out _)
 148                        Add(key, element!, options, ref state);
 149                    }
 150                }
 151            }
 152            else
 153            {
 154                // Slower path that supports continuation and reading metadata.
 155                if (state.Current.ObjectState == StackFrameObjectState.None)
 156                {
 157                    if (reader.TokenType != JsonTokenType.StartObject)
 158                    {
 159                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 160                    }
 161
 162                    state.Current.ObjectState = StackFrameObjectState.StartToken;
 163                }
 164
 165                // Handle the metadata properties.
 166                if (state.Current.CanContainMetadata && state.Current.ObjectState < StackFrameObjectState.ReadMetadata)
 167                {
 168                    if (!JsonSerializer.TryReadMetadata(this, jsonTypeInfo, ref reader, ref state))
 169                    {
 170                        value = default;
 171                        return false;
 172                    }
 173
 174                    if (state.Current.MetadataPropertyNames == MetadataPropertyName.Ref)
 175                    {
 176                        value = JsonSerializer.ResolveReferenceId<TDictionary>(ref state);
 177                        return true;
 178                    }
 179
 180                    state.Current.ObjectState = StackFrameObjectState.ReadMetadata;
 181                }
 182
 183                // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress
 184                if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 &&
 185                    state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStart
 186                    ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter)
 187                {
 188                    Debug.Assert(!IsValueType);
 189                    bool success = polymorphicConverter.OnTryReadAsObject(ref reader, polymorphicConverter.Type!, option
 190                    value = (TDictionary)objectResult!;
 191                    state.ExitPolymorphicConverter(success);
 192                    return success;
 193                }
 194
 195                // Create the dictionary.
 196                if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
 197                {
 198                    if (state.Current.CanContainMetadata)
 199                    {
 200                        JsonSerializer.ValidateMetadataForObjectConverter(ref state);
 201                    }
 202
 203                    CreateCollection(ref reader, ref state);
 204
 205                    if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0)
 206                    {
 207                        Debug.Assert(state.ReferenceId != null);
 208                        Debug.Assert(options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve);
 209                        Debug.Assert(state.Current.ReturnValue is TDictionary);
 210                        state.ReferenceResolver.AddReference(state.ReferenceId, state.Current.ReturnValue);
 211                        state.ReferenceId = null;
 212                    }
 213
 214                    jsonTypeInfo.OnDeserializing?.Invoke(state.Current.ReturnValue!);
 215
 216                    state.Current.ObjectState = StackFrameObjectState.CreatedObject;
 217                }
 218
 219                // Process all elements.
 220                _keyConverter ??= GetConverter<TKey>(keyTypeInfo);
 221                _valueConverter ??= GetConverter<TValue>(elementTypeInfo);
 222                while (true)
 223                {
 224                    if (state.Current.PropertyState == StackFramePropertyState.None)
 225                    {
 226                        // Read the key name.
 227                        if (!reader.Read())
 228                        {
 229                            value = default;
 230                            return false;
 231                        }
 232
 233                        state.Current.PropertyState = StackFramePropertyState.ReadName;
 234                    }
 235
 236                    // Determine the property.
 237                    TKey key;
 238                    if (state.Current.PropertyState < StackFramePropertyState.Name)
 239                    {
 240                        if (reader.TokenType == JsonTokenType.EndObject)
 241                        {
 242                            break;
 243                        }
 244
 245                        // Read method would have thrown if otherwise.
 246                        Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
 247
 248                        state.Current.PropertyState = StackFramePropertyState.Name;
 249
 250                        if (state.Current.CanContainMetadata)
 251                        {
 252                            ReadOnlySpan<byte> propertyName = reader.GetUnescapedSpan();
 253                            if (JsonSerializer.IsMetadataPropertyName(propertyName, state.Current.BaseJsonTypeInfo.Polym
 254                            {
 255                                if (options.AllowOutOfOrderMetadataProperties)
 256                                {
 257                                    reader.SkipWithVerify();
 258                                    state.Current.EndElement();
 259                                    continue;
 260                                }
 261                                else
 262                                {
 263                                    ThrowHelper.ThrowUnexpectedMetadataException(propertyName, ref reader, ref state);
 264                                }
 265                            }
 266                        }
 267
 268                        state.Current.JsonPropertyInfo = keyTypeInfo.PropertyInfoForTypeInfo;
 269                        key = ReadDictionaryKey(_keyConverter, ref reader, ref state, options);
 270                    }
 271                    else
 272                    {
 273                        // DictionaryKey is assigned before all return false cases, null value is unreachable
 274                        key = (TKey)state.Current.DictionaryKey!;
 275                    }
 276
 277                    if (state.Current.PropertyState < StackFramePropertyState.ReadValue)
 278                    {
 279                        if (!reader.TryAdvanceWithOptionalReadAhead(_valueConverter.RequiresReadAhead))
 280                        {
 281                            state.Current.DictionaryKey = key;
 282                            value = default;
 283                            return false;
 284                        }
 285
 286                        state.Current.PropertyState = StackFramePropertyState.ReadValue;
 287                    }
 288
 289                    if (state.Current.PropertyState < StackFramePropertyState.TryRead)
 290                    {
 291                        // Get the value from the converter and add it.
 292                        state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
 293                        bool success = _valueConverter.TryRead(ref reader, typeof(TValue), options, ref state, out TValu
 294                        if (!success)
 295                        {
 296                            state.Current.DictionaryKey = key;
 297                            value = default;
 298                            return false;
 299                        }
 300
 301                        Add(key, element!, options, ref state);
 302                        state.Current.EndElement();
 303                    }
 304                }
 305            }
 306
 307            ConvertCollection(ref state, options);
 308            object result = state.Current.ReturnValue!;
 309            jsonTypeInfo.OnDeserialized?.Invoke(result);
 310            value = (TDictionary)result;
 311
 312            return true;
 313
 314            static TKey ReadDictionaryKey(JsonConverter<TKey> keyConverter, ref Utf8JsonReader reader, scoped ref ReadSt
 315            {
 316                TKey key;
 317                string unescapedPropertyNameAsString = reader.GetString()!;
 318                state.Current.JsonPropertyNameAsString = unescapedPropertyNameAsString; // Copy key name for JSON Path s
 319
 320                // Special case string to avoid calling GetString twice and save one allocation.
 321                if (keyConverter.IsInternalConverter && keyConverter.Type == typeof(string))
 322                {
 323                    key = (TKey)(object)unescapedPropertyNameAsString;
 324                }
 325                else
 326                {
 327                    key = keyConverter.ReadAsPropertyNameCore(ref reader, keyConverter.Type, options);
 328                }
 329
 330                return key;
 331            }
 332        }
 333
 334        internal sealed override bool OnTryWrite(
 335            Utf8JsonWriter writer,
 336            TDictionary dictionary,
 337            JsonSerializerOptions options,
 338            ref WriteStack state)
 339        {
 340            if (dictionary == null)
 341            {
 342                writer.WriteNullValue();
 343                return true;
 344            }
 345
 346            JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;
 347
 348            if (!state.Current.ProcessedStartToken)
 349            {
 350                state.Current.ProcessedStartToken = true;
 351
 352                jsonTypeInfo.OnSerializing?.Invoke(dictionary);
 353
 354                writer.WriteStartObject();
 355
 356                if (state.CurrentContainsMetadata && CanHaveMetadata)
 357                {
 358                    JsonSerializer.WriteMetadataForObject(this, ref state, writer);
 359                }
 360
 361                state.Current.JsonPropertyInfo = jsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
 362            }
 363
 364            bool success = OnWriteResume(writer, dictionary, options, ref state);
 365            if (success)
 366            {
 367                if (!state.Current.ProcessedEndToken)
 368                {
 369                    state.Current.ProcessedEndToken = true;
 370                    writer.WriteEndObject();
 371                }
 372
 373                jsonTypeInfo.OnSerialized?.Invoke(dictionary);
 374            }
 375
 376            return success;
 377        }
 378    }
 379}