< Summary

Line coverage
15%
Covered lines: 69
Uncovered lines: 387
Coverable lines: 456
Total lines: 854
Line coverage: 15.1%
Branch coverage
6%
Covered branches: 9
Total branches: 136
Branch coverage: 6.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.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.ComponentModel;
 5using System.Diagnostics;
 6
 7namespace System.Text.Json.Serialization.Metadata
 8{
 9    /// <summary>
 10    /// Provides JSON serialization-related metadata about a type.
 11    /// </summary>
 12    /// <typeparam name="T">The generic definition of the type.</typeparam>
 13    public sealed partial class JsonTypeInfo<T> : JsonTypeInfo
 14    {
 15        private Action<Utf8JsonWriter, T>? _serialize;
 16
 17        private Func<T>? _typedCreateObject;
 18
 19        internal JsonTypeInfo(JsonConverter converter, JsonSerializerOptions options)
 414720            : base(typeof(T), converter, options)
 414721        {
 414722            EffectiveConverter = converter.CreateCastingConverter<T>();
 414723        }
 24
 25        /// <summary>
 26        /// A Converter whose declared type always matches that of the current JsonTypeInfo.
 27        /// It might be the same instance as JsonTypeInfo.Converter or it could be wrapped
 28        /// in a CastingConverter in cases where a polymorphic converter is being used.
 29        /// </summary>
 3034330        internal JsonConverter<T> EffectiveConverter { get; }
 31
 32        /// <summary>
 33        /// Gets or sets a parameterless factory to be used on deserialization.
 34        /// </summary>
 35        /// <exception cref="InvalidOperationException">
 36        /// The <see cref="JsonTypeInfo"/> instance has been locked for further modification.
 37        ///
 38        /// -or-
 39        ///
 40        /// A parameterless factory is not supported for the current metadata <see cref="JsonTypeInfo.Kind"/>.
 41        /// </exception>
 42        /// <remarks>
 43        /// If set to <see langword="null" />, any attempt to deserialize instances of the given type will fail at runti
 44        ///
 45        /// For contracts originating from <see cref="DefaultJsonTypeInfoResolver"/> or <see cref="JsonSerializerContext
 46        /// types with a single default constructor or default constructors annotated with <see cref="JsonConstructorAtt
 47        /// will be mapped to this delegate.
 48        /// </remarks>
 49        public new Func<T>? CreateObject
 50        {
 051            get => _typedCreateObject;
 52            set
 053            {
 054                SetCreateObject(value);
 055            }
 56        }
 57
 58        private protected override void SetCreateObject(Delegate? createObject)
 63259        {
 63260            Debug.Assert(createObject is null or Func<object> or Func<T>);
 61
 63262            VerifyMutable();
 63
 63264            if (Kind == JsonTypeInfoKind.None)
 065            {
 066                Debug.Assert(_createObject is null);
 067                Debug.Assert(_typedCreateObject is null);
 068                ThrowHelper.ThrowInvalidOperationException_JsonTypeInfoOperationNotPossibleForKind(Kind);
 69            }
 70
 63271            if (!Converter.SupportsCreateObjectDelegate)
 072            {
 073                Debug.Assert(_createObject is null);
 074                Debug.Assert(_typedCreateObject is null);
 075                ThrowHelper.ThrowInvalidOperationException_CreateObjectConverterNotCompatible(Type);
 76            }
 77
 78            Func<object>? untypedCreateObject;
 79            Func<T>? typedCreateObject;
 80
 63281            if (createObject is null)
 082            {
 083                untypedCreateObject = null;
 084                typedCreateObject = null;
 085            }
 63286            else if (createObject is Func<T> typedDelegate)
 087            {
 088                typedCreateObject = typedDelegate;
 089                untypedCreateObject = createObject is Func<object> untypedDelegate ? untypedDelegate : () => typedDelega
 090            }
 91            else
 63292            {
 63293                Debug.Assert(createObject is Func<object>);
 63294                untypedCreateObject = (Func<object>)createObject;
 63295                typedCreateObject = () => (T)untypedCreateObject();
 63296            }
 97
 63298            _createObject = untypedCreateObject;
 63299            _typedCreateObject = typedCreateObject;
 100
 101            // Clear any data related to the previously specified ctor
 632102            ConstructorAttributeProviderFactory = null;
 632103            ConstructorAttributeProvider = null;
 104
 632105            if (CreateObjectWithArgs is not null)
 0106            {
 0107                _parameterInfoValuesIndex = null;
 0108                CreateObjectWithArgs = null;
 0109                ParameterCount = 0;
 110
 0111                foreach (JsonPropertyInfo propertyInfo in PropertyList)
 0112                {
 0113                    propertyInfo.AssociatedParameter = null;
 0114                }
 0115            }
 632116        }
 117
 118        /// <summary>
 119        /// Gets or sets the strongly-typed delegate that deconstructs a union instance
 120        /// into its case type and case value.
 121        /// </summary>
 122        /// <remarks>
 123        /// <para>
 124        /// Setting this property also updates the weakly-typed
 125        /// <see cref="JsonTypeInfo.UnionDeconstructor"/> on the base type. Refer to that
 126        /// property's remarks for the full <c>(CaseType, CaseValue)</c> contract â€” including
 127        /// the role of a <see langword="null"/> <c>CaseType</c> as the discriminator for the
 128        /// canonical null-union state.
 129        /// </para>
 130        /// <para>
 131        /// For value-type unions, using this strongly-typed overload avoids boxing the union instance.
 132        /// </para>
 133        /// </remarks>
 134        public new Func<T, (Type? CaseType, object? CaseValue)>? UnionDeconstructor
 135        {
 0136            get => _typedUnionDeconstructor;
 137            set
 0138            {
 0139                VerifyMutable();
 0140                SetUnionDeconstructor(value);
 0141            }
 142        }
 143
 144        private protected override void SetUnionDeconstructor(Delegate? deconstructor)
 0145        {
 0146            Debug.Assert(deconstructor is null or Func<object, (Type?, object?)> or Func<T, (Type?, object?)>);
 147
 0148            if (deconstructor is null)
 0149            {
 0150                _unionDeconstructor = null;
 0151                _typedUnionDeconstructor = null;
 0152            }
 0153            else if (deconstructor is Func<T, (Type?, object?)> typedDelegate)
 0154            {
 0155                _typedUnionDeconstructor = typedDelegate;
 0156                _unionDeconstructor = deconstructor is Func<object, (Type?, object?)> untypedDelegate
 0157                    ? untypedDelegate
 0158                    : obj => typedDelegate((T)obj);
 0159            }
 160            else
 0161            {
 0162                Debug.Assert(deconstructor is Func<object, (Type?, object?)>);
 0163                var untypedDelegate = (Func<object, (Type?, object?)>)deconstructor;
 0164                _unionDeconstructor = untypedDelegate;
 0165                _typedUnionDeconstructor = val => untypedDelegate(val!);
 0166            }
 0167        }
 168
 169        private Func<T, (Type?, object?)>? _typedUnionDeconstructor;
 170
 171        /// <summary>
 172        /// Gets or sets the strongly-typed delegate that constructs a union instance
 173        /// from a case type and case value.
 174        /// </summary>
 175        /// <remarks>
 176        /// <para>
 177        /// Setting this property also updates the weakly-typed
 178        /// <see cref="JsonTypeInfo.UnionConstructor"/> on the base type.
 179        /// </para>
 180        /// <para>
 181        /// The delegate takes a <see cref="Type"/> parameter for disambiguation when
 182        /// overlapping case types exist (e.g., <c>Labrador : Dog</c>).
 183        /// For value-type unions, using this strongly-typed overload avoids boxing the result.
 184        /// </para>
 185        /// <para>
 186        /// The delegate is invoked with a <see langword="null"/> case value when the
 187        /// converter encounters a JSON <see cref="JsonTokenType.Null"/> token, in which
 188        /// case the case-type argument is unspecified and should be ignored. See
 189        /// <see cref="JsonTypeInfo.UnionConstructor"/> for the full null-handling contract.
 190        /// </para>
 191        /// </remarks>
 192        public new Func<Type, object?, T>? UnionConstructor
 193        {
 0194            get => _typedUnionConstructor;
 195            set
 0196            {
 0197                VerifyMutable();
 0198                SetUnionConstructor(value);
 0199            }
 200        }
 201
 202        private protected override void SetUnionConstructor(Delegate? constructor)
 0203        {
 0204            Debug.Assert(constructor is null or Func<Type, object?, object> or Func<Type, object?, T>);
 205
 0206            if (constructor is null)
 0207            {
 0208                _unionConstructor = null;
 0209                _typedUnionConstructor = null;
 0210            }
 0211            else if (constructor is Func<Type, object?, T> typedDelegate)
 0212            {
 0213                _typedUnionConstructor = typedDelegate;
 0214                _unionConstructor = constructor is Func<Type, object?, object> untypedDelegate
 0215                    ? untypedDelegate
 0216                    : (type, val) => typedDelegate(type, val)!;
 0217            }
 218            else
 0219            {
 0220                Debug.Assert(constructor is Func<Type, object?, object>);
 0221                var untypedDelegate = (Func<Type, object?, object>)constructor;
 0222                _unionConstructor = untypedDelegate;
 0223                _typedUnionConstructor = (type, val) => (T)untypedDelegate(type, val);
 0224            }
 0225        }
 226
 227        private Func<Type, object?, T>? _typedUnionConstructor;
 228
 229        /// <summary>
 230        /// Serializes an instance of <typeparamref name="T"/> using
 231        /// <see cref="JsonSourceGenerationOptionsAttribute"/> values specified at design time.
 232        /// </summary>
 233        /// <remarks>The writer is not flushed after writing.</remarks>
 234        [EditorBrowsable(EditorBrowsableState.Never)]
 235        public Action<Utf8JsonWriter, T>? SerializeHandler
 236        {
 237            get
 0238            {
 0239                return _serialize;
 0240            }
 241            internal set
 0242            {
 0243                Debug.Assert(!IsReadOnly, "We should not mutate read-only JsonTypeInfo");
 0244                _serialize = value;
 0245                HasSerializeHandler = value is not null;
 0246            }
 247        }
 248
 249        private protected override JsonPropertyInfo CreatePropertyInfoForTypeInfo()
 4147250        {
 4147251            return new JsonPropertyInfo<T>(
 4147252                declaringType: typeof(T),
 4147253                declaringTypeInfo: this,
 4147254                Options)
 4147255            {
 4147256                JsonTypeInfo = this,
 4147257                IsForTypeInfo = true,
 4147258            };
 4147259        }
 260
 261        private protected override JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo declaringTypeInfo, Type? declari
 2467262        {
 2467263            return new JsonPropertyInfo<T>(declaringType ?? declaringTypeInfo.Type, declaringTypeInfo, options)
 2467264            {
 2467265                JsonTypeInfo = this
 2467266            };
 2467267        }
 268    }
 269}
 270

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.ReadHelper.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.IO;
 7using System.IO.Pipelines;
 8using System.Threading;
 9using System.Threading.Tasks;
 10
 11namespace System.Text.Json.Serialization.Metadata
 12{
 13    public partial class JsonTypeInfo<T>
 14    {
 15        // This section provides helper methods guiding root-level deserialization
 16        // of values corresponding according to the current JsonTypeInfo configuration.
 17
 18        internal T? Deserialize(ref Utf8JsonReader reader, ref ReadStack state)
 019        {
 020            Debug.Assert(IsConfigured);
 021            bool success = EffectiveConverter.ReadCore(ref reader, out T? result, Options, ref state);
 022            Debug.Assert(success, "Should only return false for async deserialization");
 023            return result;
 024        }
 25
 26        internal async ValueTask<T?> DeserializeAsync<TReadBufferState, TStream>(TStream utf8Json, TReadBufferState buff
 27            where TReadBufferState : struct, IReadBufferState<TReadBufferState, TStream>
 2320628        {
 2320629            Debug.Assert(IsConfigured);
 2320630            JsonSerializerOptions options = Options;
 2320631            ReadStack readStack = default;
 2320632            readStack.Initialize(this, supportContinuation: true);
 2320633            var jsonReaderState = new JsonReaderState(options.GetReaderOptions());
 34
 35            try
 2320636            {
 2320637                while (true)
 2320638                {
 2320639                    bufferState = await bufferState.ReadAsync(utf8Json, cancellationToken).ConfigureAwait(false);
 2320640                    bool success = ContinueDeserialize<TReadBufferState, TStream>(
 2320641                        ref bufferState,
 2320642                        ref jsonReaderState,
 2320643                        ref readStack,
 2320644                        out T? value);
 45
 046                    if (success)
 047                    {
 048                        return value;
 49                    }
 050                }
 51            }
 52            finally
 2320653            {
 2320654                bufferState.Dispose();
 2320655            }
 056        }
 57
 58        internal ValueTask<T?> DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
 059        {
 60            // Note: The ReadBufferState ctor rents pooled buffers.
 061            StreamReadBufferState bufferState = new StreamReadBufferState(Options.DefaultBufferSize);
 062            return DeserializeAsync(utf8Json, bufferState, cancellationToken);
 063        }
 64
 65        internal ValueTask<T?> DeserializeAsync(PipeReader utf8Json, CancellationToken cancellationToken)
 066        {
 067            PipeReadBufferState bufferState = new(utf8Json);
 068            return DeserializeAsync(utf8Json, bufferState, cancellationToken);
 069        }
 70
 71        internal T? Deserialize(Stream utf8Json)
 072        {
 073            Debug.Assert(IsConfigured);
 074            JsonSerializerOptions options = Options;
 075            ReadStack readStack = default;
 076            readStack.Initialize(this, supportContinuation: true);
 077            var jsonReaderState = new JsonReaderState(options.GetReaderOptions());
 78            // Note: The ReadBufferState ctor rents pooled buffers.
 079            StreamReadBufferState bufferState = new StreamReadBufferState(options.DefaultBufferSize);
 80
 81            try
 082            {
 083                while (true)
 084                {
 085                    bufferState.Read(utf8Json);
 086                    bool success = ContinueDeserialize<StreamReadBufferState, Stream>(
 087                        ref bufferState,
 088                        ref jsonReaderState,
 089                        ref readStack,
 090                        out T? value);
 91
 092                    if (success)
 093                    {
 094                        return value;
 95                    }
 096                }
 97            }
 98            finally
 099            {
 0100                bufferState.Dispose();
 0101            }
 0102        }
 103
 104        /// <summary>
 105        /// Caches JsonTypeInfo instances used by the Serialize/DeserializeAsyncEnumerable methods.
 106        /// Store as a non-generic type to avoid triggering generic recursion in the AOT compiler.
 107        /// cf. https://github.com/dotnet/runtime/issues/85184
 108        /// </summary>
 109        internal JsonTypeInfo? _asyncEnumerableArrayTypeInfo;
 110        internal JsonTypeInfo? _asyncEnumerableRootLevelValueTypeInfo;
 111        internal JsonTypeInfo? _asyncEnumerableRootLevelSerializer;
 112
 113        internal sealed override object? DeserializeAsObject(ref Utf8JsonReader reader, ref ReadStack state)
 0114            => Deserialize(ref reader, ref state);
 115
 116        internal sealed override async ValueTask<object?> DeserializeAsObjectAsync(Stream utf8Json, CancellationToken ca
 11603117        {
 118            // Note: The ReadBufferState ctor rents pooled buffers.
 11603119            StreamReadBufferState bufferState = new StreamReadBufferState(Options.DefaultBufferSize);
 11603120            T? result = await DeserializeAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false);
 0121            return result;
 0122        }
 123
 124        internal sealed override async ValueTask<object?> DeserializeAsObjectAsync(PipeReader utf8Json, CancellationToke
 11603125        {
 11603126            T? result = await DeserializeAsync<PipeReadBufferState, PipeReader>(utf8Json, bufferState: new PipeReadBuffe
 0127            return result;
 0128        }
 129
 130        internal sealed override object? DeserializeAsObject(Stream utf8Json)
 0131            => Deserialize(utf8Json);
 132
 133        internal bool ContinueDeserialize<TReadBufferState, TStream>(
 134            ref TReadBufferState bufferState,
 135            ref JsonReaderState jsonReaderState,
 136            ref ReadStack readStack,
 137            out T? value)
 138            where TReadBufferState : struct, IReadBufferState<TReadBufferState, TStream>
 23206139        {
 23206140            bufferState.GetReader(jsonReaderState, out Utf8JsonReader reader);
 141
 142            try
 23206143            {
 23206144                bool success = EffectiveConverter.ReadCore(ref reader, out value, Options, ref readStack);
 145
 146#if DEBUG
 0147                Debug.Assert(reader.BytesConsumed <= bufferState.Bytes.Length);
 0148                Debug.Assert(!bufferState.IsFinalBlock || reader.AllowMultipleValues || reader.BytesConsumed == bufferSt
 0149                    "The reader should have thrown if we have remaining bytes.");
 150#endif
 151
 0152                jsonReaderState = reader.CurrentState;
 0153                return success;
 154            }
 155            finally
 23206156            {
 23206157                bufferState.Advance(reader.BytesConsumed);
 23206158            }
 0159        }
 160    }
 161}
 162

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.WriteHelpers.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.IO;
 6using System.IO.Pipelines;
 7using System.Text.Json.Serialization.Converters;
 8using System.Threading;
 9using System.Threading.Tasks;
 10
 11namespace System.Text.Json.Serialization.Metadata
 12{
 13    public partial class JsonTypeInfo<T>
 14    {
 15        // This section provides helper methods guiding root-level serialization
 16        // of values corresponding according to the current JsonTypeInfo configuration.
 17
 18        // Root serialization method for sync, non-streaming serialization
 19        internal void Serialize(
 20            Utf8JsonWriter writer,
 21            in T? rootValue,
 22            object? rootValueBoxed = null)
 023        {
 024            Debug.Assert(IsConfigured);
 025            Debug.Assert(rootValueBoxed is null || rootValueBoxed is T);
 26
 027            if (CanUseSerializeHandler)
 028            {
 29                // Short-circuit calls into SerializeHandler, if supported.
 30                // Even though this is already handled by JsonMetadataServicesConverter,
 31                // this avoids creating a WriteStack and calling into the converter infrastructure.
 32
 033                Debug.Assert(SerializeHandler is not null);
 034                Debug.Assert(Converter is JsonMetadataServicesConverter<T>);
 35
 036                SerializeHandler(writer, rootValue!);
 037                writer.Flush();
 038            }
 039            else if (
 040#if NET
 041                !typeof(T).IsValueType &&
 042#endif
 043                Converter.CanBePolymorphic &&
 044                rootValue is not null &&
 045                Options.TryGetPolymorphicTypeInfoForRootType(rootValue, out JsonTypeInfo? derivedTypeInfo))
 046            {
 047                Debug.Assert(typeof(T) == typeof(object));
 048                derivedTypeInfo.SerializeAsObject(writer, rootValue);
 49                // NB flushing is handled by the derived type's serialization method.
 050            }
 51            else
 052            {
 053                WriteStack state = default;
 054                state.Initialize(this, rootValueBoxed);
 55
 056                bool success = EffectiveConverter.WriteCore(writer, rootValue, Options, ref state);
 057                Debug.Assert(success);
 058                writer.Flush();
 059            }
 060        }
 61
 62        internal Task SerializeAsync(Stream utf8Json,
 63            T? rootValue,
 64            CancellationToken cancellationToken,
 65            object? rootValueBoxed = null)
 066        {
 067            PooledByteBufferWriter writer = new PooledByteBufferWriter(Options.DefaultBufferSize, utf8Json);
 68            // Value chosen as 90% of the default buffer used in PooledByteBufferWriter.
 69            // This is a tradeoff between likelihood of needing to grow the array vs. utilizing most of the buffer
 070            int flushThreshold = (int)(writer.Capacity * JsonSerializer.FlushThreshold);
 071            return SerializeAsync(writer, rootValue, flushThreshold, cancellationToken, rootValueBoxed);
 072        }
 73
 74        internal Task SerializeAsync(PipeWriter utf8Json,
 75            T? rootValue,
 76            CancellationToken cancellationToken,
 77            object? rootValueBoxed = null)
 078        {
 79            // Value chosen as 90% of 4 buffer segments in Pipes. This is semi-arbitrarily chosen and may be changed in 
 080            int flushThreshold = (int)(4 * PipeOptions.Default.MinimumSegmentSize * JsonSerializer.FlushThreshold);
 081            return SerializeAsync(utf8Json, rootValue, flushThreshold, cancellationToken, rootValueBoxed);
 082        }
 83
 84        // Root serialization method for async streaming serialization.
 85        internal async Task<FlushResult> SerializeAsync(
 86            PipeWriter pipeWriter,
 87            T? rootValue,
 88            int flushThreshold,
 89            CancellationToken cancellationToken,
 90            object? rootValueBoxed = null,
 91            Utf8JsonWriter? jsonLineWriter = null)
 092        {
 093            Debug.Assert(IsConfigured);
 094            Debug.Assert(rootValueBoxed is null || rootValueBoxed is T);
 95
 096            if (CanUseSerializeHandlerInStreaming)
 097            {
 98                // Short-circuit calls into SerializeHandler, if the `CanUseSerializeHandlerInStreaming` heuristic allow
 99
 0100                Debug.Assert(SerializeHandler is not null);
 0101                Debug.Assert(CanUseSerializeHandler);
 0102                Debug.Assert(Converter is JsonMetadataServicesConverter<T>);
 103
 0104                Utf8JsonWriter writer = jsonLineWriter ?? Utf8JsonWriterCache.RentWriter(Options, pipeWriter);
 105
 106                try
 0107                {
 108                    try
 0109                    {
 0110                        SerializeHandler(writer, rootValue!);
 0111                        writer.Flush();
 0112                        if (jsonLineWriter is not null)
 0113                        {
 0114                            WriteJsonLineTerminator(pipeWriter);
 0115                        }
 0116                    }
 117                    finally
 0118                    {
 119                        // Record the serialization size in both successful and failed operations,
 120                        // since we want to immediately opt out of the fast path if it exceeds the threshold.
 0121                        OnRootLevelAsyncSerializationCompleted(writer.BytesCommitted + writer.BytesPending);
 122
 0123                        if (jsonLineWriter is null)
 0124                        {
 0125                            Utf8JsonWriterCache.ReturnWriter(writer);
 0126                        }
 0127                    }
 128
 0129                    FlushResult result = await pipeWriter.FlushAsync(cancellationToken).ConfigureAwait(false);
 0130                    if (result.IsCanceled)
 0131                    {
 0132                        ThrowHelper.ThrowOperationCanceledException_PipeWriteCanceled();
 0133                    }
 134
 0135                    return result;
 136                }
 137                finally
 0138                {
 0139                    if (jsonLineWriter is null && pipeWriter is PooledByteBufferWriter disposable)
 0140                    {
 0141                        disposable.Dispose();
 0142                    }
 0143                }
 144            }
 0145            else if (
 0146#if NET
 0147                !typeof(T).IsValueType &&
 0148#endif
 0149                Converter.CanBePolymorphic &&
 0150                rootValue is not null &&
 0151                Options.TryGetPolymorphicTypeInfoForRootType(rootValue, out JsonTypeInfo? derivedTypeInfo))
 0152            {
 0153                Debug.Assert(typeof(T) == typeof(object));
 0154                return await derivedTypeInfo.SerializeAsObjectAsync(pipeWriter, rootValue, flushThreshold, cancellationT
 155            }
 156            else
 0157            {
 158                bool isFinalBlock;
 0159                FlushResult result = default;
 0160                WriteStack state = default;
 0161                state.Initialize(this,
 0162                    rootValueBoxed,
 0163                    supportContinuation: true,
 0164                    supportAsync: true);
 165
 0166                if (!pipeWriter.CanGetUnflushedBytes)
 0167                {
 0168                    ThrowHelper.ThrowInvalidOperationException_PipeWriterDoesNotImplementUnflushedBytes(pipeWriter);
 0169                }
 0170                state.PipeWriter = pipeWriter;
 0171                state.CancellationToken = cancellationToken;
 172
 0173                Utf8JsonWriter writer = jsonLineWriter ?? new Utf8JsonWriter(pipeWriter, Options.GetWriterOptions());
 174
 175                try
 0176                {
 0177                    state.FlushThreshold = flushThreshold;
 178
 179                    do
 0180                    {
 181                        try
 0182                        {
 0183                            isFinalBlock = EffectiveConverter.WriteCore(writer, rootValue, Options, ref state);
 184
 0185                            if (state.SuppressFlush)
 0186                            {
 0187                                Debug.Assert(!isFinalBlock);
 0188                                Debug.Assert(state.PendingTask is not null);
 0189                                state.SuppressFlush = false;
 0190                            }
 191                            else
 0192                            {
 0193                                writer.Flush();
 0194                                if (isFinalBlock && jsonLineWriter is not null)
 0195                                {
 0196                                    WriteJsonLineTerminator(pipeWriter);
 0197                                }
 198
 0199                                result = await pipeWriter.FlushAsync(cancellationToken).ConfigureAwait(false);
 0200                                if (result.IsCanceled || result.IsCompleted)
 0201                                {
 0202                                    if (result.IsCanceled)
 0203                                    {
 0204                                        ThrowHelper.ThrowOperationCanceledException_PipeWriteCanceled();
 0205                                    }
 206
 207                                    // Pipe is completed, no one is reading so no point in continuing serialization
 0208                                    return result;
 209                                }
 0210                            }
 0211                        }
 212                        finally
 0213                        {
 214                            // Await any pending resumable converter tasks (currently these can only be IAsyncEnumerator
 215                            // Note that pending tasks are always awaited, even if an exception has been thrown or the c
 0216                            if (state.PendingTask is not null)
 0217                            {
 218                                // Exceptions should only be propagated by the resuming converter
 219#if NET
 0220                                await state.PendingTask.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
 221#else
 222                                try
 223                                {
 224                                    await state.PendingTask.ConfigureAwait(false);
 225                                }
 226                                catch { }
 227#endif
 0228                            }
 0229                        }
 230
 0231                    } while (!isFinalBlock);
 232
 0233                    if (CanUseSerializeHandler)
 0234                    {
 235                        // On successful serialization, record the serialization size
 236                        // to determine potential suitability of the type for
 237                        // fast-path serialization in streaming methods.
 0238                        Debug.Assert(writer.BytesPending == 0);
 0239                        OnRootLevelAsyncSerializationCompleted(writer.BytesCommitted);
 0240                    }
 241
 0242                    return result;
 243                }
 0244                catch
 0245                {
 0246                    if (jsonLineWriter is null)
 0247                    {
 248                        // Reset the writer in exception cases as we don't want the writer.Dispose() call to flush any p
 0249                        writer.Reset();
 0250                        writer.Dispose();
 0251                    }
 252
 253                    // On exception, walk the WriteStack for any orphaned disposables and try to dispose them.
 0254                    await state.DisposePendingDisposablesOnExceptionAsync().ConfigureAwait(false);
 0255                    throw;
 256                }
 257                finally
 258                {
 0259                    if (jsonLineWriter is null)
 260                    {
 0261                        writer.Dispose();
 0262                        if (pipeWriter is PooledByteBufferWriter disposable)
 263                        {
 0264                            disposable.Dispose();
 265                        }
 0266                    }
 0267                }
 0268            }
 269
 270            static void WriteJsonLineTerminator(PipeWriter pipeWriter)
 0271            {
 272                // The JSON Lines spec mandates a single line-feed character as the line separator,
 273                // independently of any platform-specific or user-configured newline preference.
 0274                Span<byte> destination = pipeWriter.GetSpan(1);
 0275                destination[0] = (byte)'\n';
 0276                pipeWriter.Advance(1);
 0277            }
 0278        }
 279
 280        // Root serialization method for non-async streaming serialization
 281        internal void Serialize(
 282            Stream utf8Json,
 283            in T? rootValue,
 284            object? rootValueBoxed = null)
 0285        {
 0286            Debug.Assert(IsConfigured);
 0287            Debug.Assert(rootValueBoxed is null || rootValueBoxed is T);
 288
 0289            if (CanUseSerializeHandlerInStreaming)
 0290            {
 291                // Short-circuit calls into SerializeHandler, if the `CanUseSerializeHandlerInStreaming` heuristic allow
 292
 0293                Debug.Assert(SerializeHandler is not null);
 0294                Debug.Assert(CanUseSerializeHandler);
 0295                Debug.Assert(Converter is JsonMetadataServicesConverter<T>);
 296
 0297                Utf8JsonWriter writer = Utf8JsonWriterCache.RentWriterAndBuffer(Options, out PooledByteBufferWriter buff
 298                try
 0299                {
 0300                    SerializeHandler(writer, rootValue!);
 0301                    writer.Flush();
 0302                    bufferWriter.WriteToStream(utf8Json);
 0303                }
 304                finally
 0305                {
 306                    // Record the serialization size in both successful and failed operations,
 307                    // since we want to immediately opt out of the fast path if it exceeds the threshold.
 0308                    OnRootLevelAsyncSerializationCompleted(writer.BytesCommitted + writer.BytesPending);
 309
 0310                    Utf8JsonWriterCache.ReturnWriterAndBuffer(writer, bufferWriter);
 0311                }
 0312            }
 0313            else if (
 0314#if NET
 0315                !typeof(T).IsValueType &&
 0316#endif
 0317                Converter.CanBePolymorphic &&
 0318                rootValue is not null &&
 0319                Options.TryGetPolymorphicTypeInfoForRootType(rootValue, out JsonTypeInfo? polymorphicTypeInfo))
 0320            {
 0321                Debug.Assert(typeof(T) == typeof(object));
 0322                polymorphicTypeInfo.SerializeAsObject(utf8Json, rootValue);
 0323            }
 324            else
 0325            {
 326                bool isFinalBlock;
 0327                WriteStack state = default;
 0328                state.Initialize(this,
 0329                    rootValueBoxed,
 0330                    supportContinuation: true,
 0331                    supportAsync: false);
 332
 0333                Utf8JsonWriter writer = Utf8JsonWriterCache.RentWriterAndBuffer(Options, out PooledByteBufferWriter buff
 0334                Debug.Assert(bufferWriter.CanGetUnflushedBytes);
 335
 336                try
 0337                {
 0338                    state.PipeWriter = bufferWriter;
 0339                    state.FlushThreshold = (int)(bufferWriter.Capacity * JsonSerializer.FlushThreshold);
 340
 341                    do
 0342                    {
 0343                        isFinalBlock = EffectiveConverter.WriteCore(writer, rootValue, Options, ref state);
 0344                        writer.Flush();
 345
 0346                        bufferWriter.WriteToStream(utf8Json);
 0347                        bufferWriter.Clear();
 348
 0349                        Debug.Assert(state.PendingTask is null);
 0350                    } while (!isFinalBlock);
 351
 0352                    if (CanUseSerializeHandler)
 0353                    {
 354                        // On successful serialization, record the serialization size
 355                        // to determine potential suitability of the type for
 356                        // fast-path serialization in streaming methods.
 0357                        Debug.Assert(writer.BytesPending == 0);
 0358                        OnRootLevelAsyncSerializationCompleted(writer.BytesCommitted);
 0359                    }
 0360                }
 361                finally
 0362                {
 0363                    Utf8JsonWriterCache.ReturnWriterAndBuffer(writer, bufferWriter);
 0364                }
 0365            }
 0366        }
 367
 368        internal sealed override void SerializeAsObject(Utf8JsonWriter writer, object? rootValue)
 0369            => Serialize(writer, JsonSerializer.UnboxOnWrite<T>(rootValue), rootValue);
 370
 371        internal sealed override Task<FlushResult> SerializeAsObjectAsync(PipeWriter pipeWriter, object? rootValue, int 
 0372            => SerializeAsync(pipeWriter, JsonSerializer.UnboxOnWrite<T>(rootValue), flushThreshold, cancellationToken, 
 373
 374        internal sealed override Task SerializeAsObjectAsync(Stream utf8Json, object? rootValue, CancellationToken cance
 0375            => SerializeAsync(utf8Json, JsonSerializer.UnboxOnWrite<T>(rootValue), cancellationToken, rootValue);
 376
 377        internal sealed override Task SerializeAsObjectAsync(PipeWriter utf8Json, object? rootValue, CancellationToken c
 0378            => SerializeAsync(utf8Json, JsonSerializer.UnboxOnWrite<T>(rootValue), cancellationToken, rootValue);
 379
 380        internal sealed override void SerializeAsObject(Stream utf8Json, object? rootValue)
 0381            => Serialize(utf8Json, JsonSerializer.UnboxOnWrite<T>(rootValue), rootValue);
 382
 383        // Fast-path serialization in source gen has not been designed with streaming in mind.
 384        // Even though it's not used in streaming by default, we can sometimes try to turn it on
 385        // assuming that the current type is known to produce small enough JSON payloads.
 386        // The `CanUseSerializeHandlerInStreaming` flag returns true iff:
 387        //  * The type has been used in at least `MinSerializationsSampleSize` streaming serializations AND
 388        //  * No serialization size exceeding JsonSerializerOptions.DefaultBufferSize / 2 has been recorded so far.
 0389        private bool CanUseSerializeHandlerInStreaming => _canUseSerializeHandlerInStreamingState == 1;
 390        private volatile int _canUseSerializeHandlerInStreamingState; // 0: unspecified, 1: allowed, 2: forbidden
 391
 392        private const int MinSerializationsSampleSize = 10;
 393        private volatile int _serializationCount;
 394
 395        // Samples the latest serialization size for the current type to determine
 396        // if the fast-path SerializeHandler is appropriate for streaming serialization.
 397        private void OnRootLevelAsyncSerializationCompleted(long serializationSize)
 0398        {
 0399            Debug.Assert(CanUseSerializeHandler);
 400
 0401            if (_canUseSerializeHandlerInStreamingState != 2)
 0402            {
 0403                if ((ulong)serializationSize > (ulong)(Options.DefaultBufferSize / 2))
 0404                {
 405                    // We have a serialization that exceeds the buffer size --
 406                    // forbid any use future use of the fast-path handler.
 0407                    _canUseSerializeHandlerInStreamingState = 2;
 0408                }
 0409                else if ((uint)_serializationCount < MinSerializationsSampleSize)
 0410                {
 0411                    if (Interlocked.Increment(ref _serializationCount) == MinSerializationsSampleSize)
 0412                    {
 413                        // We have the minimum number of serializations needed to flag the type as safe for fast-path.
 414                        // Use CMPXCHG to avoid racing with threads reporting a large serialization.
 0415                        Interlocked.CompareExchange(ref _canUseSerializeHandlerInStreamingState, 1, 0);
 0416                    }
 0417                }
 0418            }
 0419        }
 420    }
 421}
 422

Methods/Properties

.ctor(System.Text.Json.Serialization.JsonConverter,System.Text.Json.JsonSerializerOptions)
EffectiveConverter()
CreateObject()
CreateObject(System.Func`1<T>)
SetCreateObject(System.Delegate)
UnionDeconstructor()
UnionDeconstructor(System.Func`2<T,System.ValueTuple`2<System.Type,System.Object>>)
SetUnionDeconstructor(System.Delegate)
UnionConstructor()
UnionConstructor(System.Func`3<System.Type,System.Object,T>)
SetUnionConstructor(System.Delegate)
SerializeHandler()
SerializeHandler(System.Action`2<System.Text.Json.Utf8JsonWriter,T>)
CreatePropertyInfoForTypeInfo()
CreateJsonPropertyInfo(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Type,System.Text.Json.JsonSerializerOptions)
Deserialize(System.Text.Json.Utf8JsonReader&,System.Text.Json.ReadStack&)
DeserializeAsync()
DeserializeAsync(System.IO.Stream,System.Threading.CancellationToken)
DeserializeAsync(System.IO.Pipelines.PipeReader,System.Threading.CancellationToken)
Deserialize(System.IO.Stream)
DeserializeAsObject(System.Text.Json.Utf8JsonReader&,System.Text.Json.ReadStack&)
DeserializeAsObjectAsync()
DeserializeAsObjectAsync()
DeserializeAsObject(System.IO.Stream)
ContinueDeserialize(TReadBufferState&,System.Text.Json.JsonReaderState&,System.Text.Json.ReadStack&,T&)
Serialize(System.Text.Json.Utf8JsonWriter,T&,System.Object)
SerializeAsync(System.IO.Stream,T,System.Threading.CancellationToken,System.Object)
SerializeAsync(System.IO.Pipelines.PipeWriter,T,System.Threading.CancellationToken,System.Object)
SerializeAsync()
WriteJsonLineTerminator(System.IO.Pipelines.PipeWriter)
Serialize(System.IO.Stream,T&,System.Object)
SerializeAsObject(System.Text.Json.Utf8JsonWriter,System.Object)
SerializeAsObjectAsync(System.IO.Pipelines.PipeWriter,System.Object,System.Int32,System.Threading.CancellationToken,System.Text.Json.Utf8JsonWriter)
SerializeAsObjectAsync(System.IO.Stream,System.Object,System.Threading.CancellationToken)
SerializeAsObjectAsync(System.IO.Pipelines.PipeWriter,System.Object,System.Threading.CancellationToken)
SerializeAsObject(System.IO.Stream,System.Object)
CanUseSerializeHandlerInStreaming()
OnRootLevelAsyncSerializationCompleted(System.Int64)