< Summary

Information
Line coverage
35%
Covered lines: 77
Uncovered lines: 143
Coverable lines: 220
Total lines: 348
Line coverage: 35%
Branch coverage
35%
Covered branches: 36
Total branches: 102
Branch coverage: 35.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
GetDefaultConverterStrategy()100%11100%
CreateCollection(...)0%660%
ConvertCollection(...)100%11100%
GetElementConverter(...)100%11100%
GetElementConverter(...)100%110%
OnTryRead(...)46.15%787843.2%
OnTryWrite(...)0%18180%

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\JsonCollectionConverter.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.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Text.Json.Serialization.Metadata;
 8
 9namespace System.Text.Json.Serialization
 10{
 11    /// <summary>
 12    /// Base class for all collections. Collections are assumed to implement <see cref="IEnumerable{T}"/>
 13    /// or a variant thereof e.g. <see cref="IAsyncEnumerable{T}"/>.
 14    /// </summary>
 15    internal abstract class JsonCollectionConverter<TCollection, TElement> : JsonResumableConverter<TCollection>
 16    {
 127617        internal override bool SupportsCreateObjectDelegate => true;
 63818        private protected sealed override ConverterStrategy GetDefaultConverterStrategy() => ConverterStrategy.Enumerabl
 466019        internal override Type ElementType => typeof(TElement);
 20
 21        protected abstract void Add(in TElement value, ref ReadStack state);
 22
 23        /// <summary>
 24        /// When overridden, create the collection. It may be a temporary collection or the final collection.
 25        /// </summary>
 26        protected virtual void CreateCollection(ref Utf8JsonReader reader, scoped ref ReadStack state, JsonSerializerOpt
 027        {
 028            if (state.ParentProperty?.TryGetPrePopulatedValue(ref state) == true)
 029            {
 030                return;
 31            }
 32
 033            JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;
 34
 035            if (typeInfo.CreateObject is null)
 036            {
 037                ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(typeInfo, ref reader, ref state);
 38            }
 39
 040            state.Current.ReturnValue = typeInfo.CreateObject();
 041            Debug.Assert(state.Current.ReturnValue is TCollection);
 042        }
 43
 44        /// <summary>
 45        /// When overridden, converts the temporary collection held in state.Current.ReturnValue to the final collection
 46        /// The <see cref="JsonConverter.IsConvertibleCollection"/> property must also be set to <see langword="true"/>.
 47        /// </summary>
 6848        protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }
 49
 50        protected static JsonConverter<TElement> GetElementConverter(JsonTypeInfo elementTypeInfo)
 21251        {
 21252            return ((JsonTypeInfo<TElement>)elementTypeInfo).EffectiveConverter;
 21253        }
 54
 55        protected static JsonConverter<TElement> GetElementConverter(ref WriteStack state)
 056        {
 057            Debug.Assert(state.Current.JsonPropertyInfo != null);
 058            return (JsonConverter<TElement>)state.Current.JsonPropertyInfo.EffectiveConverter;
 059        }
 60
 61        internal override bool OnTryRead(
 62            ref Utf8JsonReader reader,
 63            Type typeToConvert,
 64            JsonSerializerOptions options,
 65            scoped ref ReadStack state,
 66            [MaybeNullWhen(false)] out TCollection value)
 71267        {
 71268            JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;
 71269            JsonTypeInfo elementTypeInfo = jsonTypeInfo.ElementTypeInfo!;
 70
 71271            if (!state.SupportContinuation && !state.Current.CanContainMetadata)
 072            {
 73                // Fast path that avoids maintaining state variables and dealing with preserved references.
 74
 075                if (reader.TokenType != JsonTokenType.StartArray)
 076                {
 077                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 78                }
 79
 080                CreateCollection(ref reader, ref state, options);
 81
 082                jsonTypeInfo.OnDeserializing?.Invoke(state.Current.ReturnValue!);
 83
 084                state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
 085                JsonConverter<TElement> elementConverter = GetElementConverter(elementTypeInfo);
 086                if (elementConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
 087                {
 88                    // Fast path that avoids validation and extra indirection.
 089                    while (true)
 090                    {
 091                        reader.ReadWithVerify();
 092                        if (reader.TokenType == JsonTokenType.EndArray)
 093                        {
 094                            break;
 95                        }
 96
 97                        // Obtain the CLR value from the JSON and apply to the object.
 098                        TElement? element = elementConverter.Read(ref reader, elementConverter.Type, options);
 099                        Add(element!, ref state);
 0100                    }
 0101                }
 102                else
 0103                {
 104                    // Process all elements.
 0105                    while (true)
 0106                    {
 0107                        reader.ReadWithVerify();
 0108                        if (reader.TokenType == JsonTokenType.EndArray)
 0109                        {
 0110                            break;
 111                        }
 112
 113                        // Get the value from the converter and add it.
 0114                        elementConverter.TryRead(ref reader, typeof(TElement), options, ref state, out TElement? element
 0115                        Add(element!, ref state);
 0116                    }
 0117                }
 0118            }
 119            else
 712120            {
 121                // Slower path that supports continuation and reading metadata.
 712122                if (state.Current.ObjectState == StackFrameObjectState.None)
 712123                {
 712124                    if (reader.TokenType == JsonTokenType.StartArray)
 212125                    {
 212126                        state.Current.ObjectState = StackFrameObjectState.ReadMetadata;
 212127                    }
 500128                    else if (state.Current.CanContainMetadata)
 0129                    {
 0130                        if (reader.TokenType != JsonTokenType.StartObject)
 0131                        {
 0132                            ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 133                        }
 134
 0135                        state.Current.ObjectState = StackFrameObjectState.StartToken;
 0136                    }
 137                    else
 500138                    {
 500139                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 140                    }
 212141                }
 142
 143                // Handle the metadata properties.
 212144                if (state.Current.CanContainMetadata && state.Current.ObjectState < StackFrameObjectState.ReadMetadata)
 0145                {
 0146                    if (!JsonSerializer.TryReadMetadata(this, jsonTypeInfo, ref reader, ref state))
 0147                    {
 0148                        value = default;
 0149                        return false;
 150                    }
 151
 0152                    if (state.Current.MetadataPropertyNames == MetadataPropertyName.Ref)
 0153                    {
 0154                        value = JsonSerializer.ResolveReferenceId<TCollection>(ref state);
 0155                        return true;
 156                    }
 157
 0158                    state.Current.ObjectState = StackFrameObjectState.ReadMetadata;
 0159                }
 160
 161                // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress
 212162                if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 &&
 212163                    state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStart
 212164                    ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter)
 0165                {
 0166                    Debug.Assert(!IsValueType);
 0167                    bool success = polymorphicConverter.OnTryReadAsObject(ref reader, polymorphicConverter.Type!, option
 0168                    value = (TCollection)objectResult!;
 0169                    state.ExitPolymorphicConverter(success);
 0170                    return success;
 171                }
 172
 212173                if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
 212174                {
 212175                    if (state.Current.CanContainMetadata)
 0176                    {
 0177                        JsonSerializer.ValidateMetadataForArrayConverter(this, ref reader, ref state);
 0178                    }
 179
 212180                    CreateCollection(ref reader, ref state, options);
 181
 212182                    if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0)
 0183                    {
 0184                        Debug.Assert(state.ReferenceId != null);
 0185                        Debug.Assert(options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve);
 0186                        Debug.Assert(state.Current.ReturnValue is TCollection);
 0187                        state.ReferenceResolver.AddReference(state.ReferenceId, state.Current.ReturnValue);
 0188                        state.ReferenceId = null;
 0189                    }
 190
 212191                    jsonTypeInfo.OnDeserializing?.Invoke(state.Current.ReturnValue!);
 192
 212193                    state.Current.ObjectState = StackFrameObjectState.CreatedObject;
 212194                }
 195
 212196                if (state.Current.ObjectState < StackFrameObjectState.ReadElements)
 212197                {
 212198                    JsonConverter<TElement> elementConverter = GetElementConverter(elementTypeInfo);
 212199                    state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
 200
 201                    // Process all elements.
 222202                    while (true)
 222203                    {
 222204                        if (state.Current.PropertyState < StackFramePropertyState.ReadValue)
 222205                        {
 222206                            if (!reader.TryAdvanceWithOptionalReadAhead(elementConverter.RequiresReadAhead))
 0207                            {
 0208                                value = default;
 0209                                return false;
 210                            }
 211
 94212                            state.Current.PropertyState = StackFramePropertyState.ReadValue;
 94213                        }
 214
 94215                        if (state.Current.PropertyState < StackFramePropertyState.ReadValueIsEnd)
 94216                        {
 94217                            if (reader.TokenType == JsonTokenType.EndArray)
 34218                            {
 34219                                break;
 220                            }
 221
 60222                            state.Current.PropertyState = StackFramePropertyState.ReadValueIsEnd;
 60223                        }
 224
 60225                        if (state.Current.PropertyState < StackFramePropertyState.TryRead)
 60226                        {
 227                            // Get the value from the converter and add it.
 60228                            if (!elementConverter.TryRead(ref reader, typeof(TElement), options, ref state, out TElement
 0229                            {
 0230                                value = default;
 0231                                return false;
 232                            }
 233
 10234                            Add(element!, ref state);
 235
 236                            // No need to set PropertyState to TryRead since we're done with this element now.
 10237                            state.Current.EndElement();
 10238                        }
 10239                    }
 240
 34241                    state.Current.ObjectState = StackFrameObjectState.ReadElements;
 34242                }
 243
 34244                if (state.Current.ObjectState < StackFrameObjectState.EndToken)
 34245                {
 246                    // Array payload is nested inside a $values metadata property.
 34247                    if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Values) != 0)
 0248                    {
 0249                        if (!reader.Read())
 0250                        {
 0251                            value = default;
 0252                            return false;
 253                        }
 0254                    }
 255
 34256                    state.Current.ObjectState = StackFrameObjectState.EndToken;
 34257                }
 258
 34259                if (state.Current.ObjectState < StackFrameObjectState.EndTokenValidation)
 34260                {
 261                    // Array payload is nested inside a $values metadata property.
 34262                    if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Values) != 0)
 0263                    {
 0264                        if (reader.TokenType != JsonTokenType.EndObject)
 0265                        {
 0266                            Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
 0267                            if (options.AllowOutOfOrderMetadataProperties)
 0268                            {
 0269                                Debug.Assert(JsonSerializer.IsMetadataPropertyName(reader.GetUnescapedSpan(), (state.Cur
 0270                                bool result = reader.TrySkipPartial(reader.CurrentDepth - 1); // skip to the end of the 
 0271                                Debug.Assert(result, "Metadata reader must have buffered all contents.");
 0272                                Debug.Assert(reader.TokenType is JsonTokenType.EndObject);
 0273                            }
 274                            else
 0275                            {
 0276                                ThrowHelper.ThrowJsonException_MetadataInvalidPropertyInArrayMetadata(ref state, typeToC
 277                            }
 0278                        }
 0279                    }
 34280                }
 34281            }
 282
 34283            ConvertCollection(ref state, options);
 34284            object returnValue = state.Current.ReturnValue!;
 34285            jsonTypeInfo.OnDeserialized?.Invoke(returnValue);
 34286            value = (TCollection)returnValue;
 287
 34288            return true;
 34289        }
 290
 291        internal override bool OnTryWrite(
 292            Utf8JsonWriter writer,
 293            TCollection value,
 294            JsonSerializerOptions options,
 295            ref WriteStack state)
 0296        {
 297            bool success;
 298
 0299            if (value == null)
 0300            {
 0301                writer.WriteNullValue();
 0302                success = true;
 0303            }
 304            else
 0305            {
 0306                JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;
 307
 0308                if (!state.Current.ProcessedStartToken)
 0309                {
 0310                    state.Current.ProcessedStartToken = true;
 311
 0312                    jsonTypeInfo.OnSerializing?.Invoke(value);
 313
 0314                    if (state.CurrentContainsMetadata && CanHaveMetadata)
 0315                    {
 0316                        state.Current.MetadataPropertyName = JsonSerializer.WriteMetadataForCollection(this, ref state, 
 0317                    }
 318
 319                    // Writing the start of the array must happen after any metadata
 0320                    writer.WriteStartArray();
 0321                    state.Current.JsonPropertyInfo = jsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
 0322                }
 323
 0324                success = OnWriteResume(writer, value, options, ref state);
 0325                if (success)
 0326                {
 0327                    if (!state.Current.ProcessedEndToken)
 0328                    {
 0329                        state.Current.ProcessedEndToken = true;
 0330                        writer.WriteEndArray();
 331
 0332                        if (state.Current.MetadataPropertyName != 0)
 0333                        {
 334                            // Write the EndObject for $values.
 0335                            writer.WriteEndObject();
 0336                        }
 0337                    }
 338
 0339                    jsonTypeInfo.OnSerialized?.Invoke(value);
 0340                }
 0341            }
 342
 0343            return success;
 0344        }
 345
 346        protected abstract bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, r
 347    }
 348}