< Summary

Information
Class: System.Text.Json.Serialization.Converters.ObjectDefaultConverter<T>
Assembly: System.Text.Json
File(s): C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\Converters\Object\ObjectDefaultConverter.cs
Line coverage
11%
Covered lines: 36
Uncovered lines: 287
Coverable lines: 323
Total lines: 503
Line coverage: 11.1%
Branch coverage
14%
Covered branches: 20
Total branches: 136
Branch coverage: 14.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
OnTryRead(...)26.31%767620.11%
PopulatePropertiesFastPath(...)0%880%
OnTryWrite(...)0%46460%
ReadPropertyValue(...)0%440%
ReadAheadPropertyValue(...)0%220%

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\Converters\Object\ObjectDefaultConverter.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.Runtime.CompilerServices;
 8using System.Text.Json.Serialization.Metadata;
 9
 10namespace System.Text.Json.Serialization.Converters
 11{
 12    /// <summary>
 13    /// Default base class implementation of <cref>JsonObjectConverter{T}</cref>.
 14    /// </summary>
 15    internal class ObjectDefaultConverter<T> : JsonObjectConverter<T> where T : notnull
 16    {
 017        internal override bool CanHaveMetadata => true;
 184918        internal override bool SupportsCreateObjectDelegate => true;
 19
 20        internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, s
 14021        {
 14022            JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;
 23
 24            object obj;
 25
 14026            if (!state.SupportContinuation && !state.Current.CanContainMetadata)
 027            {
 28                // Fast path that avoids maintaining state variables and dealing with preserved references.
 29
 030                if (reader.TokenType != JsonTokenType.StartObject)
 031                {
 032                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 33                }
 34
 035                if (state.ParentProperty?.TryGetPrePopulatedValue(ref state) == true)
 036                {
 037                    obj = state.Current.ReturnValue!;
 038                }
 39                else
 040                {
 041                    if (jsonTypeInfo.CreateObject == null)
 042                    {
 043                        ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(jsonTypeInfo, ref reader, ref st
 44                    }
 45
 046                    obj = jsonTypeInfo.CreateObject();
 047                }
 48
 049                PopulatePropertiesFastPath(obj, jsonTypeInfo, options, ref reader, ref state);
 050                Debug.Assert(obj != null);
 051                value = (T)obj;
 052                return true;
 53            }
 54            else
 14055            {
 56                // Slower path that supports continuation and reading metadata.
 57
 14058                if (state.Current.ObjectState == StackFrameObjectState.None)
 14059                {
 14060                    if (reader.TokenType != JsonTokenType.StartObject)
 11861                    {
 11862                        ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 63                    }
 64
 2265                    state.Current.ObjectState = StackFrameObjectState.StartToken;
 2266                }
 67
 68                // Handle the metadata properties.
 2269                if (state.Current.CanContainMetadata && state.Current.ObjectState < StackFrameObjectState.ReadMetadata)
 070                {
 071                    if (!JsonSerializer.TryReadMetadata(this, jsonTypeInfo, ref reader, ref state))
 072                    {
 073                        value = default;
 074                        return false;
 75                    }
 76
 077                    if (state.Current.MetadataPropertyNames == MetadataPropertyName.Ref)
 078                    {
 079                        value = JsonSerializer.ResolveReferenceId<T>(ref state);
 080                        return true;
 81                    }
 82
 083                    state.Current.ObjectState = StackFrameObjectState.ReadMetadata;
 084                }
 85
 86                // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress
 2287                if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 &&
 2288                    state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStart
 2289                    ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter)
 090                {
 091                    Debug.Assert(!IsValueType);
 092                    bool success = polymorphicConverter.OnTryReadAsObject(ref reader, polymorphicConverter.Type!, option
 093                    value = (T)objectResult!;
 094                    state.ExitPolymorphicConverter(success);
 095                    return success;
 96                }
 97
 2298                if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
 2299                {
 22100                    if (state.Current.CanContainMetadata)
 0101                    {
 0102                        JsonSerializer.ValidateMetadataForObjectConverter(ref state);
 0103                    }
 104
 22105                    if (state.Current.MetadataPropertyNames == MetadataPropertyName.Ref)
 0106                    {
 0107                        value = JsonSerializer.ResolveReferenceId<T>(ref state);
 0108                        return true;
 109                    }
 110
 22111                    if (state.ParentProperty?.TryGetPrePopulatedValue(ref state) == true)
 0112                    {
 0113                        obj = state.Current.ReturnValue!;
 0114                    }
 115                    else
 22116                    {
 22117                        if (jsonTypeInfo.CreateObject == null)
 0118                        {
 0119                            ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(jsonTypeInfo, ref reader, re
 120                        }
 121
 22122                        obj = jsonTypeInfo.CreateObject();
 22123                    }
 124
 22125                    if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0)
 0126                    {
 0127                        Debug.Assert(state.ReferenceId != null);
 0128                        Debug.Assert(options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve);
 0129                        state.ReferenceResolver.AddReference(state.ReferenceId, obj);
 0130                        state.ReferenceId = null;
 0131                    }
 132
 22133                    jsonTypeInfo.OnDeserializing?.Invoke(obj);
 134
 22135                    state.Current.ReturnValue = obj;
 22136                    state.Current.ObjectState = StackFrameObjectState.CreatedObject;
 22137                    state.Current.InitializePropertiesValidationState(jsonTypeInfo);
 22138                }
 139                else
 0140                {
 0141                    obj = state.Current.ReturnValue!;
 0142                    Debug.Assert(obj != null);
 0143                }
 144
 145                // Process all properties.
 22146                while (true)
 22147                {
 148                    // Determine the property.
 22149                    if (state.Current.PropertyState == StackFramePropertyState.None)
 22150                    {
 22151                        if (!reader.Read())
 0152                        {
 0153                            state.Current.ReturnValue = obj;
 0154                            value = default;
 0155                            return false;
 156                        }
 157
 0158                        state.Current.PropertyState = StackFramePropertyState.ReadName;
 0159                    }
 160
 161                    JsonPropertyInfo jsonPropertyInfo;
 162
 0163                    if (state.Current.PropertyState < StackFramePropertyState.Name)
 0164                    {
 0165                        JsonTokenType tokenType = reader.TokenType;
 0166                        if (tokenType == JsonTokenType.EndObject)
 0167                        {
 0168                            break;
 169                        }
 170
 171                        // Read method would have thrown if otherwise.
 0172                        Debug.Assert(tokenType == JsonTokenType.PropertyName);
 173
 0174                        jsonTypeInfo.ValidateCanBeUsedForPropertyMetadataSerialization();
 0175                        ReadOnlySpan<byte> unescapedPropertyName = JsonSerializer.GetPropertyName(ref state, ref reader,
 0176                        if (isAlreadyReadMetadataProperty)
 0177                        {
 0178                            Debug.Assert(options.AllowOutOfOrderMetadataProperties);
 0179                            reader.SkipWithVerify();
 0180                            state.Current.EndProperty();
 0181                            continue;
 182                        }
 183
 0184                        jsonPropertyInfo = JsonSerializer.LookupProperty(
 0185                            obj,
 0186                            unescapedPropertyName,
 0187                            ref state,
 0188                            options,
 0189                            out bool useExtensionProperty);
 190
 0191                        state.Current.UseExtensionProperty = useExtensionProperty;
 0192                        state.Current.PropertyState = StackFramePropertyState.Name;
 0193                    }
 194                    else
 0195                    {
 0196                        Debug.Assert(state.Current.JsonPropertyInfo != null);
 0197                        jsonPropertyInfo = state.Current.JsonPropertyInfo!;
 0198                    }
 199
 0200                    if (state.Current.PropertyState < StackFramePropertyState.ReadValue)
 0201                    {
 0202                        if (!jsonPropertyInfo.CanDeserializeOrPopulate)
 0203                        {
 0204                            if (!reader.TrySkipPartial(targetDepth: state.Current.OriginalDepth + 1))
 0205                            {
 0206                                state.Current.ReturnValue = obj;
 0207                                value = default;
 0208                                return false;
 209                            }
 210
 0211                            state.Current.EndProperty();
 0212                            continue;
 213                        }
 214
 0215                        if (!ReadAheadPropertyValue(ref state, ref reader, jsonPropertyInfo))
 0216                        {
 0217                            state.Current.ReturnValue = obj;
 0218                            value = default;
 0219                            return false;
 220                        }
 221
 0222                        state.Current.PropertyState = StackFramePropertyState.ReadValue;
 0223                    }
 224
 0225                    if (state.Current.PropertyState < StackFramePropertyState.TryRead)
 0226                    {
 227                        // Obtain the CLR value from the JSON and set the member.
 0228                        if (!state.Current.UseExtensionProperty)
 0229                        {
 0230                            if (!jsonPropertyInfo.ReadJsonAndSetMember(obj, ref state, ref reader))
 0231                            {
 0232                                state.Current.ReturnValue = obj;
 0233                                value = default;
 0234                                return false;
 235                            }
 0236                        }
 237                        else
 0238                        {
 0239                            if (!jsonPropertyInfo.ReadJsonAndAddExtensionProperty(obj, ref state, ref reader))
 0240                            {
 241                                // No need to set 'value' here since JsonElement must be read in full.
 0242                                state.Current.ReturnValue = obj;
 0243                                value = default;
 0244                                return false;
 245                            }
 0246                        }
 247
 0248                        state.Current.EndProperty();
 0249                    }
 0250                }
 0251            }
 252
 0253            jsonTypeInfo.OnDeserialized?.Invoke(obj);
 0254            state.Current.ValidateAllRequiredPropertiesAreRead(jsonTypeInfo);
 255
 256            // Unbox
 0257            Debug.Assert(obj != null);
 0258            value = (T)obj;
 259
 260            // Check if we are trying to update the UTF-8 property cache.
 0261            if (state.Current.PropertyRefCacheBuilder != null)
 0262            {
 0263                jsonTypeInfo.UpdateUtf8PropertyCache(ref state.Current);
 0264            }
 265
 0266            return true;
 0267        }
 268
 269        // This method is using aggressive inlining to avoid extra stack frame for deep object graphs.
 270        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 271        internal static void PopulatePropertiesFastPath(object obj, JsonTypeInfo jsonTypeInfo, JsonSerializerOptions opt
 0272        {
 0273            jsonTypeInfo.OnDeserializing?.Invoke(obj);
 0274            state.Current.InitializePropertiesValidationState(jsonTypeInfo);
 275
 276            // Process all properties.
 0277            while (true)
 0278            {
 279                // Read the property name or EndObject.
 0280                reader.ReadWithVerify();
 281
 0282                JsonTokenType tokenType = reader.TokenType;
 283
 0284                if (tokenType == JsonTokenType.EndObject)
 0285                {
 0286                    break;
 287                }
 288
 289                // Read method would have thrown if otherwise.
 0290                Debug.Assert(tokenType == JsonTokenType.PropertyName);
 291
 0292                ReadOnlySpan<byte> unescapedPropertyName = JsonSerializer.GetPropertyName(ref state, ref reader, options
 0293                Debug.Assert(!isAlreadyReadMetadataProperty, "Only possible for types that can read metadata, which do n
 294
 0295                jsonTypeInfo.ValidateCanBeUsedForPropertyMetadataSerialization();
 0296                JsonPropertyInfo jsonPropertyInfo = JsonSerializer.LookupProperty(
 0297                    obj,
 0298                    unescapedPropertyName,
 0299                    ref state,
 0300                    options,
 0301                    out bool useExtensionProperty);
 302
 0303                ReadPropertyValue(obj, ref state, ref reader, jsonPropertyInfo, useExtensionProperty);
 0304            }
 305
 0306            jsonTypeInfo.OnDeserialized?.Invoke(obj);
 0307            state.Current.ValidateAllRequiredPropertiesAreRead(jsonTypeInfo);
 308
 309            // Check if we are trying to update the UTF-8 property cache.
 0310            if (state.Current.PropertyRefCacheBuilder != null)
 0311            {
 0312                jsonTypeInfo.UpdateUtf8PropertyCache(ref state.Current);
 0313            }
 0314        }
 315
 316        internal sealed override bool OnTryWrite(
 317            Utf8JsonWriter writer,
 318            T value,
 319            JsonSerializerOptions options,
 320            ref WriteStack state)
 0321        {
 0322            JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;
 0323            jsonTypeInfo.ValidateCanBeUsedForPropertyMetadataSerialization();
 324
 0325            object obj = value; // box once
 326
 0327            if (!state.SupportContinuation)
 0328            {
 0329                jsonTypeInfo.OnSerializing?.Invoke(obj);
 330
 0331                writer.WriteStartObject();
 332
 0333                if (state.CurrentContainsMetadata && CanHaveMetadata)
 0334                {
 0335                    JsonSerializer.WriteMetadataForObject(this, ref state, writer);
 0336                }
 337
 0338                foreach (JsonPropertyInfo jsonPropertyInfo in jsonTypeInfo.PropertyCache)
 0339                {
 0340                    if (jsonPropertyInfo.CanSerialize)
 0341                    {
 342                        // Remember the current property for JsonPath support if an exception is thrown.
 0343                        state.Current.JsonPropertyInfo = jsonPropertyInfo;
 0344                        state.Current.NumberHandling = jsonPropertyInfo.EffectiveNumberHandling;
 345
 0346                        bool success = jsonPropertyInfo.GetMemberAndWriteJson(obj, ref state, writer);
 347                        // Converters only return 'false' when out of data which is not possible in fast path.
 0348                        Debug.Assert(success);
 349
 0350                        state.Current.EndProperty();
 0351                    }
 0352                }
 353
 354                // Write extension data after the normal properties.
 0355                JsonPropertyInfo? extensionDataProperty = jsonTypeInfo.ExtensionDataProperty;
 0356                if (extensionDataProperty?.CanSerialize == true)
 0357                {
 358                    // Remember the current property for JsonPath support if an exception is thrown.
 0359                    state.Current.JsonPropertyInfo = extensionDataProperty;
 0360                    state.Current.NumberHandling = extensionDataProperty.EffectiveNumberHandling;
 361
 0362                    bool success = extensionDataProperty.GetMemberAndWriteJsonExtensionData(obj, ref state, writer);
 0363                    Debug.Assert(success);
 364
 0365                    state.Current.EndProperty();
 0366                }
 367
 0368                writer.WriteEndObject();
 0369            }
 370            else
 0371            {
 0372                if (!state.Current.ProcessedStartToken)
 0373                {
 0374                    writer.WriteStartObject();
 375
 0376                    if (state.CurrentContainsMetadata && CanHaveMetadata)
 0377                    {
 0378                        JsonSerializer.WriteMetadataForObject(this, ref state, writer);
 0379                    }
 380
 0381                    jsonTypeInfo.OnSerializing?.Invoke(obj);
 382
 0383                    state.Current.ProcessedStartToken = true;
 0384                }
 385
 0386                ReadOnlySpan<JsonPropertyInfo> propertyCache = jsonTypeInfo.PropertyCache;
 0387                while (state.Current.EnumeratorIndex < propertyCache.Length)
 0388                {
 0389                    JsonPropertyInfo jsonPropertyInfo = propertyCache[state.Current.EnumeratorIndex];
 0390                    if (jsonPropertyInfo.CanSerialize)
 0391                    {
 0392                        state.Current.JsonPropertyInfo = jsonPropertyInfo;
 0393                        state.Current.NumberHandling = jsonPropertyInfo.EffectiveNumberHandling;
 394
 0395                        if (!jsonPropertyInfo.GetMemberAndWriteJson(obj!, ref state, writer))
 0396                        {
 0397                            Debug.Assert(jsonPropertyInfo.EffectiveConverter.ConverterStrategy != ConverterStrategy.Valu
 0398                            return false;
 399                        }
 400
 0401                        state.Current.EndProperty();
 0402                        state.Current.EnumeratorIndex++;
 403
 0404                        if (ShouldFlush(ref state, writer))
 0405                        {
 0406                            return false;
 407                        }
 0408                    }
 409                    else
 0410                    {
 0411                        state.Current.EnumeratorIndex++;
 0412                    }
 0413                }
 414
 415                // Write extension data after the normal properties.
 0416                if (state.Current.EnumeratorIndex == propertyCache.Length)
 0417                {
 0418                    JsonPropertyInfo? extensionDataProperty = jsonTypeInfo.ExtensionDataProperty;
 0419                    if (extensionDataProperty?.CanSerialize == true)
 0420                    {
 421                        // Remember the current property for JsonPath support if an exception is thrown.
 0422                        state.Current.JsonPropertyInfo = extensionDataProperty;
 0423                        state.Current.NumberHandling = extensionDataProperty.EffectiveNumberHandling;
 424
 0425                        if (!extensionDataProperty.GetMemberAndWriteJsonExtensionData(obj, ref state, writer))
 0426                        {
 0427                            return false;
 428                        }
 429
 0430                        state.Current.EndProperty();
 0431                        state.Current.EnumeratorIndex++;
 432
 0433                        if (ShouldFlush(ref state, writer))
 0434                        {
 0435                            return false;
 436                        }
 0437                    }
 438                    else
 0439                    {
 0440                        state.Current.EnumeratorIndex++;
 0441                    }
 0442                }
 443
 0444                if (!state.Current.ProcessedEndToken)
 0445                {
 0446                    state.Current.ProcessedEndToken = true;
 0447                    writer.WriteEndObject();
 0448                }
 0449            }
 450
 0451            jsonTypeInfo.OnSerialized?.Invoke(obj);
 452
 0453            return true;
 0454        }
 455
 456        // AggressiveInlining since this method is only called from two locations and is on a hot path.
 457        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 458        protected static void ReadPropertyValue(
 459            object obj,
 460            scoped ref ReadStack state,
 461            ref Utf8JsonReader reader,
 462            JsonPropertyInfo jsonPropertyInfo,
 463            bool useExtensionProperty)
 0464        {
 465            // Skip the property if not found.
 0466            if (!jsonPropertyInfo.CanDeserializeOrPopulate)
 0467            {
 468                // The Utf8JsonReader.Skip() method will fail fast if it detects that we're reading
 469                // from a partially read buffer, regardless of whether the next value is available.
 470                // This can result in erroneous failures in cases where a custom converter is calling
 471                // into a built-in converter (cf. https://github.com/dotnet/runtime/issues/74108).
 472                // For this reason we need to call the TrySkip() method instead -- the serializer
 473                // should guarantee sufficient read-ahead has been performed for the current object.
 0474                bool success = reader.TrySkip();
 0475                Debug.Assert(success, "Serializer should guarantee sufficient read-ahead has been done.");
 0476            }
 477            else
 0478            {
 479                // Set the property value.
 0480                reader.ReadWithVerify();
 481
 0482                if (!useExtensionProperty)
 0483                {
 0484                    jsonPropertyInfo.ReadJsonAndSetMember(obj, ref state, ref reader);
 0485                }
 486                else
 0487                {
 0488                    jsonPropertyInfo.ReadJsonAndAddExtensionProperty(obj, ref state, ref reader);
 0489                }
 0490            }
 491
 492            // Ensure any exception thrown in the next read does not have a property in its JsonPath.
 0493            state.Current.EndProperty();
 0494        }
 495
 496        protected static bool ReadAheadPropertyValue(scoped ref ReadStack state, ref Utf8JsonReader reader, JsonProperty
 0497        {
 498            // Extension properties can use the JsonElement converter and thus require read-ahead.
 0499            bool requiresReadAhead = jsonPropertyInfo.EffectiveConverter.RequiresReadAhead || state.Current.UseExtension
 0500            return reader.TryAdvanceWithOptionalReadAhead(requiresReadAhead);
 0501        }
 502    }
 503}