< Summary

Line coverage
30%
Covered lines: 131
Uncovered lines: 299
Coverable lines: 430
Total lines: 847
Line coverage: 30.4%
Branch coverage
31%
Covered branches: 59
Total branches: 189
Branch coverage: 31.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\JsonConverterOfT.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.Converters;
 7using System.Text.Json.Serialization.Metadata;
 8
 9namespace System.Text.Json.Serialization
 10{
 11    /// <summary>
 12    /// Converts an object or value to or from JSON.
 13    /// </summary>
 14    /// <typeparam name="T">The <see cref="System.Type"/> to convert.</typeparam>
 15    public abstract partial class JsonConverter<T> : JsonConverter
 16    {
 17        /// <summary>
 18        /// When overridden, constructs a new <see cref="JsonConverter{T}"/> instance.
 19        /// </summary>
 287420        protected internal JsonConverter()
 287421        {
 287422            IsValueType = typeof(T).IsValueType;
 23
 287424            if (HandleNull)
 325            {
 326                HandleNullOnRead = true;
 327                HandleNullOnWrite = true;
 328            }
 287129            else if (UsesDefaultHandleNull)
 57030            {
 31                // If the type doesn't support null, allow the converter a chance to modify.
 32                // These semantics are backwards compatible with 3.0.
 57033                HandleNullOnRead = default(T) is not null;
 34
 35                // The framework handles null automatically on writes.
 57036                HandleNullOnWrite = false;
 57037            }
 287438        }
 39
 40        /// <summary>
 41        /// Determines whether the type can be converted.
 42        /// </summary>
 43        /// <remarks>
 44        /// The default implementation is to return True when <paramref name="typeToConvert"/> equals typeof(T).
 45        /// </remarks>
 46        /// <param name="typeToConvert"></param>
 47        /// <returns>True if the type can be converted, False otherwise.</returns>
 48        public override bool CanConvert(Type typeToConvert)
 049        {
 050            return typeToConvert == typeof(T);
 051        }
 52
 57253        private protected override ConverterStrategy GetDefaultConverterStrategy() => ConverterStrategy.Value;
 54
 55        internal sealed override JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions options)
 1225456        {
 1225457            return new JsonTypeInfo<T>(this, options);
 1225458        }
 59
 60        /// <summary>
 61        /// Indicates whether <see langword="null"/> should be passed to the converter on serialization,
 62        /// and whether <see cref="JsonTokenType.Null"/> should be passed on deserialization.
 63        /// </summary>
 64        /// <remarks>
 65        /// The default value is <see langword="true"/> for converters based on value types, and <see langword="false"/>
 66        /// </remarks>
 67        public virtual bool HandleNull
 68        {
 69            get
 57070            {
 57071                UsesDefaultHandleNull = true;
 57072                return false;
 57073            }
 74        }
 75
 76        // This non-generic API is sealed as it just forwards to the generic version.
 77        internal sealed override void WriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
 078        {
 079            T valueOfT = JsonSerializer.UnboxOnWrite<T>(value)!;
 080            Write(writer, valueOfT, options);
 081        }
 82
 83        // This non-generic API is sealed as it just forwards to the generic version.
 84        internal sealed override bool OnTryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions opt
 085        {
 086            T valueOfT = JsonSerializer.UnboxOnWrite<T>(value)!;
 087            return OnTryWrite(writer, valueOfT, options, ref state);
 088        }
 89
 90        // This non-generic API is sealed as it just forwards to the generic version.
 91        internal sealed override void WriteAsPropertyNameAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOp
 092        {
 093            T valueOfT = JsonSerializer.UnboxOnWrite<T>(value)!;
 094            WriteAsPropertyName(writer, valueOfT, options);
 095        }
 96
 97        internal sealed override void WriteAsPropertyNameCoreAsObject(Utf8JsonWriter writer, object? value, JsonSerializ
 098        {
 099            T valueOfT = JsonSerializer.UnboxOnWrite<T>(value)!;
 0100            WriteAsPropertyNameCore(writer, valueOfT, options, isWritingExtensionDataProperty);
 0101        }
 102
 103        internal sealed override void WriteNumberWithCustomHandlingAsObject(Utf8JsonWriter writer, object? value, JsonNu
 0104        {
 0105            T valueOfT = JsonSerializer.UnboxOnWrite<T>(value)!;
 0106            WriteNumberWithCustomHandling(writer, valueOfT, handling);
 0107        }
 108
 109        // This non-generic API is sealed as it just forwards to the generic version.
 110        internal sealed override bool TryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions optio
 0111        {
 0112            T valueOfT = JsonSerializer.UnboxOnWrite<T>(value)!;
 0113            return TryWrite(writer, valueOfT, options, ref state);
 0114        }
 115
 116        // Provide a default implementation for value converters.
 117        internal virtual bool OnTryWrite(Utf8JsonWriter writer,
 118#nullable disable // T may or may not be nullable depending on the derived converter's HandleNull override.
 119            T value,
 120#nullable enable
 121            JsonSerializerOptions options,
 122            ref WriteStack state)
 0123        {
 0124            Write(writer, value, options);
 0125            return true;
 0126        }
 127
 128        // Provide a default implementation for value converters.
 129        internal virtual bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, sc
 0130        {
 0131            value = Read(ref reader, typeToConvert, options);
 0132            return true;
 0133        }
 134
 135        /// <summary>
 136        /// Read and convert the JSON to T.
 137        /// </summary>
 138        /// <remarks>
 139        /// A converter may throw any Exception, but should throw <cref>JsonException</cref> when the JSON is invalid.
 140        /// </remarks>
 141        /// <param name="reader">The <see cref="Utf8JsonReader"/> to read from.</param>
 142        /// <param name="typeToConvert">The <see cref="System.Type"/> being converted.</param>
 143        /// <param name="options">The <see cref="JsonSerializerOptions"/> being used.</param>
 144        /// <returns>The value that was converted.</returns>
 145        /// <remarks>Note that the value of <seealso cref="HandleNull"/> determines if the converter handles null JSON t
 146        public abstract T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options);
 147
 148        internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref R
 26054149        {
 150            // For perf and converter simplicity, handle null here instead of forwarding to the converter.
 26054151            if (reader.TokenType == JsonTokenType.Null && !HandleNullOnRead && !state.IsContinuation)
 0152            {
 0153                if (default(T) is not null)
 0154                {
 0155                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
 156                }
 157
 0158                value = default;
 0159                isPopulatedValue = false;
 0160                return true;
 161            }
 162
 26054163            if (ConverterStrategy == ConverterStrategy.Value)
 23684164            {
 165                // A value converter should never be within a continuation.
 23684166                Debug.Assert(!state.IsContinuation);
 167#if !DEBUG
 168                // For performance, only perform validation on internal converters on debug builds.
 169                if (IsInternalConverter)
 170                {
 171                    if (state.Current.NumberHandling != null && IsInternalConverterForNumberType)
 172                    {
 173                        value = ReadNumberWithCustomHandling(ref reader, state.Current.NumberHandling.Value, options);
 174                    }
 175                    else
 176                    {
 177                        value = Read(ref reader, typeToConvert, options);
 178                    }
 179                }
 180                else
 181#endif
 23684182                {
 23684183                    JsonTokenType originalPropertyTokenType = reader.TokenType;
 23684184                    int originalPropertyDepth = reader.CurrentDepth;
 23684185                    long originalPropertyBytesConsumed = reader.BytesConsumed;
 186
 23684187                    if (state.Current.NumberHandling != null && IsInternalConverterForNumberType)
 6770188                    {
 6770189                        value = ReadNumberWithCustomHandling(ref reader, state.Current.NumberHandling.Value, options);
 1166190                    }
 191                    else
 16914192                    {
 16914193                        value = Read(ref reader, typeToConvert, options);
 2806194                    }
 195
 3972196                    VerifyRead(
 3972197                        originalPropertyTokenType,
 3972198                        originalPropertyDepth,
 3972199                        originalPropertyBytesConsumed,
 3972200                        isValueConverter: true,
 3972201                        ref reader);
 3972202                }
 203
 3972204                isPopulatedValue = false;
 3972205                return true;
 206            }
 207
 2370208            Debug.Assert(IsInternalConverter);
 2370209            bool isContinuation = state.IsContinuation;
 210            bool success;
 211
 2370212            if (
 2370213#if NET
 2370214                !typeof(T).IsValueType &&
 2370215#endif
 2370216                CanBePolymorphic)
 634217            {
 218                // Special case object converters since they don't
 219                // require the expensive ReadStack.Push()/Pop() operations.
 634220                Debug.Assert(this is ObjectConverter);
 634221                success = OnTryRead(ref reader, typeToConvert, options, ref state, out value);
 428222                Debug.Assert(success);
 428223                isPopulatedValue = false;
 428224                return true;
 225            }
 226
 1736227            JsonPropertyInfo? propertyInfo = state.Current.JsonPropertyInfo;
 1736228            object? parentObj = state.Current.ReturnValue;
 229
 230#if DEBUG
 231            // DEBUG: ensure push/pop operations preserve stack integrity
 1736232            JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo;
 233#endif
 1736234            state.Push();
 1736235            Debug.Assert(Type == state.Current.JsonTypeInfo.Type);
 236
 1736237            if (!isContinuation)
 1736238            {
 239#if DEBUG
 240                // For performance, only perform token type validation of converters on debug builds.
 1736241                Debug.Assert(state.Current.OriginalTokenType == JsonTokenType.None);
 1736242                state.Current.OriginalTokenType = reader.TokenType;
 243#endif
 1736244                Debug.Assert(state.Current.OriginalDepth == 0);
 1736245                state.Current.OriginalDepth = reader.CurrentDepth;
 1736246            }
 247
 1736248            if (parentObj != null && propertyInfo != null && !propertyInfo.IsForTypeInfo)
 0249            {
 0250                state.Current.HasParentObject = true;
 0251            }
 252
 1736253            success = OnTryRead(ref reader, typeToConvert, options, ref state, out value);
 254#if DEBUG
 34255            if (success)
 34256            {
 34257                if (state.IsContinuation)
 0258                {
 259                    // The resumable converter did not forward to the next converter that previously returned false.
 0260                    ThrowHelper.ThrowJsonException_SerializationConverterRead(this);
 261                }
 262
 34263                VerifyRead(
 34264                    state.Current.OriginalTokenType,
 34265                    state.Current.OriginalDepth,
 34266                    bytesConsumed: 0,
 34267                    isValueConverter: false,
 34268                    ref reader);
 269
 270                // No need to clear state.Current.* since a stack pop will occur.
 34271            }
 272#endif
 273
 34274            isPopulatedValue = state.Current.IsPopulating;
 34275            state.Pop(success);
 276#if DEBUG
 34277            Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo));
 278#endif
 34279            return success;
 4434280        }
 281
 282        internal sealed override bool OnTryReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOpt
 0283        {
 0284            bool success = OnTryRead(ref reader, typeToConvert, options, ref state, out T? typedValue);
 0285            value = typedValue;
 0286            return success;
 0287        }
 288
 289        internal sealed override bool TryReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptio
 0290        {
 0291            bool success = TryRead(ref reader, typeToConvert, options, ref state, out T? typedValue, out _);
 0292            value = typedValue;
 0293            return success;
 0294        }
 295
 296        internal sealed override object? ReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptio
 0297        {
 0298            T? typedValue = Read(ref reader, typeToConvert, options);
 0299            return typedValue;
 0300        }
 301
 302        internal sealed override object? ReadAsPropertyNameAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonS
 0303        {
 0304            T typedValue = ReadAsPropertyName(ref reader, typeToConvert, options);
 0305            return typedValue;
 0306        }
 307
 308        internal sealed override object? ReadAsPropertyNameCoreAsObject(ref Utf8JsonReader reader, Type typeToConvert, J
 0309        {
 0310            T typedValue = ReadAsPropertyNameCore(ref reader, typeToConvert, options);
 0311            return typedValue;
 0312        }
 313
 314        internal sealed override object? ReadNumberWithCustomHandlingAsObject(ref Utf8JsonReader reader, JsonNumberHandl
 0315        {
 0316            T typedValue = ReadNumberWithCustomHandling(ref reader, handling, options);
 0317            return typedValue;
 0318        }
 319
 320        /// <summary>
 321        /// Performance optimization.
 322        /// The 'in' modifier in 'TryWrite(in T Value)' causes boxing for Nullable{T}, so this helper avoids that.
 323        /// TODO: Remove this work-around once https://github.com/dotnet/runtime/issues/50915 is addressed.
 324        /// </summary>
 0325        private static bool IsNull(T? value) => value is null;
 326
 327        internal bool TryWrite(Utf8JsonWriter writer, in T? value, JsonSerializerOptions options, ref WriteStack state)
 0328        {
 0329            if (writer.CurrentDepth >= options.EffectiveMaxDepth)
 0330            {
 0331                ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth);
 332            }
 333
 0334            if (default(T) is null && !HandleNullOnWrite && IsNull(value))
 0335            {
 336                // We do not pass null values to converters unless HandleNullOnWrite is true. Null values for properties
 337                // already handled in GetMemberAndWriteJson() so we don't need to check for IgnoreNullValues here.
 0338                writer.WriteNullValue();
 0339                return true;
 340            }
 341
 0342            if (ConverterStrategy == ConverterStrategy.Value)
 0343            {
 0344                Debug.Assert(!state.IsContinuation);
 345
 0346                int originalPropertyDepth = writer.CurrentDepth;
 347
 0348                if (state.Current.NumberHandling != null && IsInternalConverterForNumberType)
 0349                {
 0350                    WriteNumberWithCustomHandling(writer, value, state.Current.NumberHandling.Value);
 0351                }
 352                else
 0353                {
 0354                    Write(writer, value, options);
 0355                }
 356
 0357                VerifyWrite(originalPropertyDepth, writer);
 0358                return true;
 359            }
 360
 0361            Debug.Assert(IsInternalConverter);
 0362            bool isContinuation = state.IsContinuation;
 363            bool success;
 364
 0365            if (
 0366#if NET
 0367                // Short-circuit the check against "is not null"; treated as a constant by recent versions of the JIT.
 0368                !typeof(T).IsValueType &&
 0369#else
 0370                !IsValueType &&
 0371#endif
 0372                value is not null &&
 0373                // Do not handle objects that have already been
 0374                // handled by a polymorphic converter for a base type.
 0375                state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted)
 0376            {
 0377                JsonTypeInfo jsonTypeInfo = state.PeekNestedJsonTypeInfo();
 0378                Debug.Assert(jsonTypeInfo.Converter.Type == Type);
 379
 0380                bool canBePolymorphic = CanBePolymorphic || jsonTypeInfo.PolymorphicTypeResolver is not null;
 0381                JsonConverter? polymorphicConverter = canBePolymorphic ?
 0382                    ResolvePolymorphicConverter(value, jsonTypeInfo, options, ref state) :
 0383                    null;
 384
 0385                if (!isContinuation && options.ReferenceHandlingStrategy != JsonKnownReferenceHandler.Unspecified &&
 0386                    TryHandleSerializedObjectReference(writer, value, options, polymorphicConverter, ref state))
 0387                {
 388                    // The reference handler wrote reference metadata, serialization complete.
 0389                    return true;
 390                }
 391
 0392                if (polymorphicConverter is not null)
 0393                {
 0394                    success = polymorphicConverter.TryWriteAsObject(writer, value, options, ref state);
 0395                    state.Current.ExitPolymorphicConverter(success);
 396
 0397                    if (success)
 0398                    {
 0399                        if (state.Current.IsPushedReferenceForCycleDetection)
 0400                        {
 0401                            state.ReferenceResolver.PopReferenceForCycleDetection();
 0402                            state.Current.IsPushedReferenceForCycleDetection = false;
 0403                        }
 0404                    }
 405
 0406                    return success;
 407                }
 0408            }
 409
 410#if DEBUG
 411            // DEBUG: ensure push/pop operations preserve stack integrity
 0412            JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo;
 413#endif
 0414            state.Push();
 0415            Debug.Assert(Type == state.Current.JsonTypeInfo.Type);
 416
 417#if DEBUG
 418            // For performance, only perform validation on internal converters on debug builds.
 0419            if (!isContinuation)
 0420            {
 0421                Debug.Assert(state.Current.OriginalDepth == 0);
 0422                state.Current.OriginalDepth = writer.CurrentDepth;
 0423            }
 424#endif
 0425            success = OnTryWrite(writer, value, options, ref state);
 426#if DEBUG
 0427            if (success)
 0428            {
 0429                VerifyWrite(state.Current.OriginalDepth, writer);
 0430            }
 431#endif
 0432            state.Pop(success);
 433
 0434            if (success && state.Current.IsPushedReferenceForCycleDetection)
 0435            {
 0436                state.ReferenceResolver.PopReferenceForCycleDetection();
 0437                state.Current.IsPushedReferenceForCycleDetection = false;
 0438            }
 439#if DEBUG
 0440            Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo));
 441#endif
 0442            return success;
 0443        }
 444
 445        internal bool TryWriteDataExtensionProperty(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref W
 0446        {
 0447            Debug.Assert(value != null);
 448
 0449            if (!IsInternalConverter)
 0450            {
 0451                return TryWrite(writer, value, options, ref state);
 452            }
 453
 0454            JsonDictionaryConverter<T>? dictionaryConverter = this as JsonDictionaryConverter<T>
 0455                ?? (this as JsonMetadataServicesConverter<T>)?.Converter as JsonDictionaryConverter<T>;
 456
 0457            if (dictionaryConverter == null)
 0458            {
 459                // If not JsonDictionaryConverter<T> then we are JsonObject.
 460                // Avoid a type reference to JsonObject and its converter to support trimming.
 461                // The WriteExtensionDataValue virtual method is overridden by the JsonObject converter.
 0462                Debug.Assert(Type == typeof(Nodes.JsonObject));
 0463                WriteExtensionDataValue(writer, value!, options);
 464
 0465                return true;
 466            }
 467
 0468            if (writer.CurrentDepth >= options.EffectiveMaxDepth)
 0469            {
 0470                ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth);
 471            }
 472
 0473            bool isContinuation = state.IsContinuation;
 474            bool success;
 475
 0476            state.Push();
 477
 0478            if (!isContinuation)
 0479            {
 0480                Debug.Assert(state.Current.OriginalDepth == 0);
 0481                state.Current.OriginalDepth = writer.CurrentDepth;
 0482            }
 483
 484            // Extension data properties change how dictionary key naming policies are applied.
 0485            state.Current.IsWritingExtensionDataProperty = true;
 0486            state.Current.JsonPropertyInfo = state.Current.JsonTypeInfo.ElementTypeInfo!.PropertyInfoForTypeInfo;
 487
 0488            success = dictionaryConverter.OnWriteResume(writer, value, options, ref state);
 0489            if (success)
 0490            {
 0491                VerifyWrite(state.Current.OriginalDepth, writer);
 0492            }
 493
 0494            state.Pop(success);
 495
 0496            return success;
 0497        }
 498
 499        /// <summary>
 500        /// Used to support JsonObject as an extension property in a loosely-typed, trimmable manner.
 501        /// </summary>
 502        /// <remarks>
 503        /// Writes the extension data contents without wrapping object braces.
 504        /// </remarks>
 505        internal virtual void WriteExtensionDataValue(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
 0506        {
 0507            Debug.Fail("Should not be reachable.");
 508
 509            throw new InvalidOperationException();
 510        }
 511
 512        /// <inheritdoc/>
 37846513        public sealed override Type Type { get; } = typeof(T);
 514
 515        internal void VerifyRead(JsonTokenType tokenType, int depth, long bytesConsumed, bool isValueConverter, ref Utf8
 4006516        {
 4006517            Debug.Assert(isValueConverter == (ConverterStrategy == ConverterStrategy.Value));
 518
 4006519            switch (tokenType)
 520            {
 521                case JsonTokenType.StartArray:
 128522                    if (reader.TokenType != JsonTokenType.EndArray)
 0523                    {
 0524                        ThrowHelper.ThrowJsonException_SerializationConverterRead(this);
 525                    }
 128526                    else if (depth != reader.CurrentDepth)
 0527                    {
 0528                        ThrowHelper.ThrowJsonException_SerializationConverterRead(this);
 529                    }
 530
 128531                    break;
 532
 533                case JsonTokenType.StartObject:
 0534                    if (reader.TokenType != JsonTokenType.EndObject)
 0535                    {
 0536                        ThrowHelper.ThrowJsonException_SerializationConverterRead(this);
 537                    }
 0538                    else if (depth != reader.CurrentDepth)
 0539                    {
 0540                        ThrowHelper.ThrowJsonException_SerializationConverterRead(this);
 541                    }
 542
 0543                    break;
 544
 545                case JsonTokenType.None:
 0546                    Debug.Assert(IsRootLevelMultiContentStreamingConverter);
 0547                    break;
 548
 549                default:
 3878550                    if (isValueConverter)
 3878551                    {
 552                        // A value converter should not make any reads.
 3878553                        if (reader.BytesConsumed != bytesConsumed)
 0554                        {
 0555                            ThrowHelper.ThrowJsonException_SerializationConverterRead(this);
 556                        }
 3878557                    }
 558                    else
 0559                    {
 560                        // A non-value converter (object or collection) should always have Start and End tokens
 561                        // unless it is polymorphic or supports null value reads.
 0562                        if (!CanBePolymorphic && !(HandleNullOnRead && tokenType == JsonTokenType.Null))
 0563                        {
 0564                            ThrowHelper.ThrowJsonException_SerializationConverterRead(this);
 565                        }
 0566                    }
 567
 568                    // Should not be possible to change token type.
 3878569                    Debug.Assert(reader.TokenType == tokenType);
 3878570                    break;
 571            }
 4006572        }
 573
 574        internal void VerifyWrite(int originalDepth, Utf8JsonWriter writer)
 0575        {
 0576            if (originalDepth != writer.CurrentDepth)
 0577            {
 0578                ThrowHelper.ThrowJsonException_SerializationConverterWrite(this);
 579            }
 0580        }
 581
 582        /// <summary>
 583        /// Write the value as JSON.
 584        /// </summary>
 585        /// <remarks>
 586        /// A converter may throw any Exception, but should throw <cref>JsonException</cref> when the JSON
 587        /// cannot be created.
 588        /// </remarks>
 589        /// <param name="writer">The <see cref="Utf8JsonWriter"/> to write to.</param>
 590        /// <param name="value">The value to convert. Note that the value of <seealso cref="HandleNull"/> determines if 
 591        /// <param name="options">The <see cref="JsonSerializerOptions"/> being used.</param>
 592        public abstract void Write(
 593            Utf8JsonWriter writer,
 594#nullable disable // T may or may not be nullable depending on the derived converter's HandleNull override.
 595            T value,
 596#nullable restore
 597            JsonSerializerOptions options);
 598
 599        /// <summary>
 600        /// Reads a dictionary key from a JSON property name.
 601        /// </summary>
 602        /// <param name="reader">The <see cref="Utf8JsonReader"/> to read from.</param>
 603        /// <param name="typeToConvert">The <see cref="System.Type"/> being converted.</param>
 604        /// <param name="options">The <see cref="JsonSerializerOptions"/> being used.</param>
 605        /// <returns>The value that was converted.</returns>
 606        /// <remarks>Method should be overridden in custom converters of types used in deserialized dictionary keys.</re
 607        public virtual T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options
 0608        {
 609            // .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
 0610            JsonConverter<T>? fallbackConverter = GetFallbackConverterForPropertyNameSerialization(options);
 0611            if (fallbackConverter is null)
 0612            {
 0613                ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(Type, this);
 614            }
 615
 0616            return fallbackConverter.ReadAsPropertyNameCore(ref reader, typeToConvert, options);
 0617        }
 618
 619        internal virtual T ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions o
 0620        {
 0621            Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
 622
 0623            long originalBytesConsumed = reader.BytesConsumed;
 0624            T result = ReadAsPropertyName(ref reader, typeToConvert, options);
 0625            if (reader.BytesConsumed != originalBytesConsumed)
 0626            {
 0627                ThrowHelper.ThrowJsonException_SerializationConverterRead(this);
 628            }
 629
 0630            return result;
 0631        }
 632
 633        /// <summary>
 634        /// Writes a dictionary key as a JSON property name.
 635        /// </summary>
 636        /// <param name="writer">The <see cref="Utf8JsonWriter"/> to write to.</param>
 637        /// <param name="value">The value to convert. Note that the value of <seealso cref="HandleNull"/> determines if 
 638        /// <param name="options">The <see cref="JsonSerializerOptions"/> being used.</param>
 639        /// <remarks>Method should be overridden in custom converters of types used in serialized dictionary keys.</rema
 640        public virtual void WriteAsPropertyName(Utf8JsonWriter writer, [DisallowNull] T value, JsonSerializerOptions opt
 0641        {
 642            // .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
 0643            JsonConverter<T>? fallbackConverter = GetFallbackConverterForPropertyNameSerialization(options);
 0644            if (fallbackConverter is null)
 0645            {
 0646                ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(Type, this);
 647            }
 648
 0649            fallbackConverter.WriteAsPropertyNameCore(writer, value, options, isWritingExtensionDataProperty: false);
 0650        }
 651
 652        internal virtual void WriteAsPropertyNameCore(Utf8JsonWriter writer, [DisallowNull] T value, JsonSerializerOptio
 0653        {
 0654            ArgumentNullException.ThrowIfNull(value);
 655
 0656            if (isWritingExtensionDataProperty)
 0657            {
 658                // Extension data is meant as mechanism to gather unused JSON properties;
 659                // do not apply any custom key conversions and hardcode the default behavior.
 0660                Debug.Assert(!IsInternalConverter && Type == typeof(string));
 0661                writer.WritePropertyName((string)(object)value!);
 0662                return;
 663            }
 664
 0665            int originalDepth = writer.CurrentDepth;
 0666            WriteAsPropertyName(writer, value, options);
 0667            if (originalDepth != writer.CurrentDepth || writer.TokenType != JsonTokenType.PropertyName)
 0668            {
 0669                ThrowHelper.ThrowJsonException_SerializationConverterWrite(this);
 670            }
 0671        }
 672
 673        // .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
 674        private JsonConverter<T>? GetFallbackConverterForPropertyNameSerialization(JsonSerializerOptions options)
 0675        {
 0676            JsonConverter<T>? result = null;
 677
 678            // For consistency do not return any default converters for options instances linked to a
 679            // JsonSerializerContext, even if the default converters might have been rooted.
 0680            if (!IsInternalConverter && options.TypeInfoResolver is not JsonSerializerContext)
 0681            {
 0682                result = _fallbackConverterForPropertyNameSerialization;
 683
 0684                if (result is null && DefaultJsonTypeInfoResolver.TryGetDefaultSimpleConverter(Type, out JsonConverter? 
 0685                {
 0686                    Debug.Assert(defaultConverter != this);
 0687                    _fallbackConverterForPropertyNameSerialization = result = (JsonConverter<T>)defaultConverter;
 0688                }
 0689            }
 690
 0691            return result;
 0692        }
 693
 694        private JsonConverter<T>? _fallbackConverterForPropertyNameSerialization;
 695
 696        internal virtual T ReadNumberWithCustomHandling(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSeri
 0697            => throw new InvalidOperationException();
 698
 699        internal virtual void WriteNumberWithCustomHandling(Utf8JsonWriter writer, T? value, JsonNumberHandling handling
 0700            => throw new InvalidOperationException();
 701    }
 702}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\JsonConverterOfT.ReadCore.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;
 5
 6namespace System.Text.Json.Serialization
 7{
 8    public partial class JsonConverter<T>
 9    {
 10        internal bool ReadCore(
 11            ref Utf8JsonReader reader,
 12            out T? value,
 13            JsonSerializerOptions options,
 14            ref ReadStack state)
 5592415        {
 16            try
 5592417            {
 5592418                if (!state.IsContinuation && !IsRootLevelMultiContentStreamingConverter)
 5592419                {
 20                    // This is first call to the converter -- advance the reader
 21                    // to the first JSON token and perform a read-ahead if necessary.
 5592422                    if (!reader.TryAdvanceWithOptionalReadAhead(RequiresReadAhead))
 023                    {
 024                        if (state.SupportContinuation && state.Current.ReturnValue is object result)
 025                        {
 26                            // This branch is hit when deserialization has completed in an earlier call
 27                            // but we're still processing trailing whitespace. Return the result stored in the state mac
 028                            Debug.Assert(!reader.AllowMultipleValues, "should not be entered by converters allowing mult
 029                            value = (T)result;
 030                        }
 31                        else
 032                        {
 033                            value = default;
 034                        }
 35
 036                        return reader.IsFinalBlock;
 37                    }
 2599438                }
 39
 2599440                bool success = TryRead(ref reader, state.Current.JsonTypeInfo.Type, options, ref state, out value, out _
 442441                if (success)
 442442                {
 442443                    if (!reader.AllowMultipleValues)
 442444                    {
 45                        // Read any trailing whitespace. This will throw if JsonCommentHandling=Disallow.
 46                        // Avoiding setting ReturnValue for the final block; reader.Read() returns 'false' even when thi
 442447                        if (!reader.Read() && !reader.IsFinalBlock)
 048                        {
 49                            // This method will re-enter if so set `ReturnValue` which will be returned during re-entry.
 050                            state.Current.ReturnValue = value;
 051                            success = false;
 052                        }
 17453                    }
 17454                }
 55
 17456                return success;
 57            }
 5575058            catch (Exception ex)
 5575059            {
 5575060                switch (ex)
 61                {
 62                    case JsonReaderException jsonReaderEx:
 3564063                        ThrowHelper.ReThrowWithPath(ref state, jsonReaderEx);
 64                        break;
 65
 465466                    case FormatException when ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException:
 465467                        ThrowHelper.ReThrowWithPath(ref state, reader, ex);
 68                        break;
 69
 1289470                    case InvalidOperationException when ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonExce
 1287271                        ThrowHelper.ReThrowWithPath(ref state, reader, ex);
 72                        break;
 73
 256274                    case JsonException jsonEx when jsonEx.Path is null:
 75                        // JsonExceptions where the Path property is already set
 76                        // typically originate from nested calls to JsonSerializer;
 77                        // treat these cases as any other exception type and do not
 78                        // overwrite any exception information.
 256279                        ThrowHelper.AddJsonExceptionInformation(ref state, reader, jsonEx);
 256280                        break;
 81
 082                    case NotSupportedException when !ex.Message.Contains(" Path: "):
 83                        // If the message already contains Path, just re-throw. This could occur in serializer re-entry 
 84                        // To get proper Path semantics in re-entry cases, APIs that take 'state' need to be used.
 085                        ThrowHelper.ThrowNotSupportedException(ref state, reader, ex);
 86                        break;
 87                }
 88
 258489                throw;
 90            }
 17491        }
 92    }
 93}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\JsonConverterOfT.WriteCore.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
 4namespace System.Text.Json.Serialization
 5{
 6    public partial class JsonConverter<T>
 7    {
 8        internal bool WriteCore(
 9            Utf8JsonWriter writer,
 10            in T? value,
 11            JsonSerializerOptions options,
 12            ref WriteStack state)
 013        {
 14            try
 015            {
 016                return TryWrite(writer, value, options, ref state);
 17            }
 018            catch (Exception ex)
 019            {
 020                if (!state.SupportAsync)
 021                {
 22                    // Async serializers should dispose sync and
 23                    // async disposables from the async root method.
 024                    state.DisposePendingDisposablesOnException();
 025                }
 26
 027                switch (ex)
 28                {
 029                    case InvalidOperationException when ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonExce
 030                        ThrowHelper.ReThrowWithPath(ref state, ex);
 31                        break;
 32
 33                    case JsonException { Path: null } jsonException:
 34                        // JsonExceptions where the Path property is already set
 35                        // typically originate from nested calls to JsonSerializer;
 36                        // treat these cases as any other exception type and do not
 37                        // overwrite any exception information.
 038                        ThrowHelper.AddJsonExceptionInformation(ref state, jsonException);
 039                        break;
 40
 041                    case NotSupportedException when !ex.Message.Contains(" Path: "):
 42                        // If the message already contains Path, just re-throw. This could occur in serializer re-entry 
 43                        // To get proper Path semantics in re-entry cases, APIs that take 'state' need to be used.
 044                        ThrowHelper.ThrowNotSupportedException(ref state, ex);
 45                        break;
 46                }
 47
 048                throw;
 49            }
 050        }
 51    }
 52}

Methods/Properties

.ctor()
CanConvert(System.Type)
GetDefaultConverterStrategy()
CreateJsonTypeInfo(System.Text.Json.JsonSerializerOptions)
HandleNull()
WriteAsObject(System.Text.Json.Utf8JsonWriter,System.Object,System.Text.Json.JsonSerializerOptions)
OnTryWriteAsObject(System.Text.Json.Utf8JsonWriter,System.Object,System.Text.Json.JsonSerializerOptions,System.Text.Json.WriteStack&)
WriteAsPropertyNameAsObject(System.Text.Json.Utf8JsonWriter,System.Object,System.Text.Json.JsonSerializerOptions)
WriteAsPropertyNameCoreAsObject(System.Text.Json.Utf8JsonWriter,System.Object,System.Text.Json.JsonSerializerOptions,System.Boolean)
WriteNumberWithCustomHandlingAsObject(System.Text.Json.Utf8JsonWriter,System.Object,System.Text.Json.Serialization.JsonNumberHandling)
TryWriteAsObject(System.Text.Json.Utf8JsonWriter,System.Object,System.Text.Json.JsonSerializerOptions,System.Text.Json.WriteStack&)
OnTryWrite(System.Text.Json.Utf8JsonWriter,T,System.Text.Json.JsonSerializerOptions,System.Text.Json.WriteStack&)
OnTryRead(System.Text.Json.Utf8JsonReader&,System.Type,System.Text.Json.JsonSerializerOptions,System.Text.Json.ReadStack&,T&)
TryRead(System.Text.Json.Utf8JsonReader&,System.Type,System.Text.Json.JsonSerializerOptions,System.Text.Json.ReadStack&,T&,System.Boolean&)
OnTryReadAsObject(System.Text.Json.Utf8JsonReader&,System.Type,System.Text.Json.JsonSerializerOptions,System.Text.Json.ReadStack&,System.Object&)
TryReadAsObject(System.Text.Json.Utf8JsonReader&,System.Type,System.Text.Json.JsonSerializerOptions,System.Text.Json.ReadStack&,System.Object&)
ReadAsObject(System.Text.Json.Utf8JsonReader&,System.Type,System.Text.Json.JsonSerializerOptions)
ReadAsPropertyNameAsObject(System.Text.Json.Utf8JsonReader&,System.Type,System.Text.Json.JsonSerializerOptions)
ReadAsPropertyNameCoreAsObject(System.Text.Json.Utf8JsonReader&,System.Type,System.Text.Json.JsonSerializerOptions)
ReadNumberWithCustomHandlingAsObject(System.Text.Json.Utf8JsonReader&,System.Text.Json.Serialization.JsonNumberHandling,System.Text.Json.JsonSerializerOptions)
IsNull(T)
TryWrite(System.Text.Json.Utf8JsonWriter,T&,System.Text.Json.JsonSerializerOptions,System.Text.Json.WriteStack&)
TryWriteDataExtensionProperty(System.Text.Json.Utf8JsonWriter,T,System.Text.Json.JsonSerializerOptions,System.Text.Json.WriteStack&)
WriteExtensionDataValue(System.Text.Json.Utf8JsonWriter,T,System.Text.Json.JsonSerializerOptions)
Type()
VerifyRead(System.Text.Json.JsonTokenType,System.Int32,System.Int64,System.Boolean,System.Text.Json.Utf8JsonReader&)
VerifyWrite(System.Int32,System.Text.Json.Utf8JsonWriter)
ReadAsPropertyName(System.Text.Json.Utf8JsonReader&,System.Type,System.Text.Json.JsonSerializerOptions)
ReadAsPropertyNameCore(System.Text.Json.Utf8JsonReader&,System.Type,System.Text.Json.JsonSerializerOptions)
WriteAsPropertyName(System.Text.Json.Utf8JsonWriter,T,System.Text.Json.JsonSerializerOptions)
WriteAsPropertyNameCore(System.Text.Json.Utf8JsonWriter,T,System.Text.Json.JsonSerializerOptions,System.Boolean)
GetFallbackConverterForPropertyNameSerialization(System.Text.Json.JsonSerializerOptions)
ReadNumberWithCustomHandling(System.Text.Json.Utf8JsonReader&,System.Text.Json.Serialization.JsonNumberHandling,System.Text.Json.JsonSerializerOptions)
WriteNumberWithCustomHandling(System.Text.Json.Utf8JsonWriter,T,System.Text.Json.Serialization.JsonNumberHandling)
ReadCore(System.Text.Json.Utf8JsonReader&,T&,System.Text.Json.JsonSerializerOptions,System.Text.Json.ReadStack&)
WriteCore(System.Text.Json.Utf8JsonWriter,T&,System.Text.Json.JsonSerializerOptions,System.Text.Json.WriteStack&)