< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 864
Coverable lines: 864
Total lines: 2055
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 306
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: GetTypeInfo(...)0%220%
File 1: TryGetTypeInfo(...)0%220%
File 1: GetTypeInfo()100%110%
File 1: TryGetTypeInfo(...)100%110%
File 1: GetTypeInfoInternal(...)0%18180%
File 1: TryGetTypeInfoCached(...)0%220%
File 1: GetTypeInfoForRootType(...)0%440%
File 1: TryGetPolymorphicTypeInfoForRootType(...)0%440%
File 1: ClearCaches()0%220%
File 1: .ctor(...)100%110%
File 1: GetOrAddTypeInfo(...)0%440%
File 1: TryGetTypeInfo(...)0%220%
File 1: Clear()100%110%
File 1: GetOrAddCacheEntry(...)100%110%
File 1: CreateCacheEntry(...)100%110%
File 1: FallBackToNearestAncestor(...)0%440%
File 1: DetermineNearestAncestor(...)0%16160%
File 1: .ctor(...)100%110%
File 1: .ctor(...)100%110%
File 1: GetResult()0%220%
File 1: .cctor()100%110%
File 1: GetOrCreate(...)0%10100%
File 1: TryGetContext(...)0%12120%
File 1: Equals(...)0%60600%
File 1: CompareLists(System.Text.Json.Serialization.ConfigurationList`1<TValue>,System.Text.Json.Serialization.ConfigurationList`1<TValue>)0%12120%
File 1: GetHashCode(...)100%110%
File 1: AddListHashCode(System.HashCode&,System.Text.Json.Serialization.ConfigurationList`1<TValue>)0%440%
File 1: AddHashCode(System.HashCode&,TValue)0%220%
File 2: GetConverter(...)0%440%
File 2: GetConverterInternal(...)100%110%
File 2: GetConverterFromList(...)0%660%
File 2: GetTypeClassifierFromList(...)0%660%
File 2: ExpandConverterFactory(...)0%220%
File 2: CheckConverterNullabilityIsSameAsPropertyType(...)0%660%
File 3: .ctor()100%110%
File 3: .ctor(...)0%440%
File 3: .ctor(...)0%660%
File 3: TrackOptionsInstance(...)100%110%
File 3: .cctor()100%110%
File 3: AddContext()100%110%
File 3: MakeReadOnly()0%220%
File 3: MakeReadOnly(...)0%440%
File 3: ConfigureForJsonSerializer()0%18180%
File 3: GetTypeInfoNoCaching(...)0%12120%
File 3: GetDocumentOptions()100%110%
File 3: GetNodeOptions()100%110%
File 3: GetReaderOptions()100%110%
File 3: GetWriterOptions()100%110%
File 3: GetWriterOptionsForJsonLines()100%110%
File 3: VerifyMutable()0%220%
File 3: .ctor(...)100%110%
File 3: OnCollectionModifying()100%110%
File 3: .ctor(...)100%110%
File 3: OnCollectionModifying()100%110%
File 3: .ctor(...)100%110%
File 3: DetachFromOptions()100%110%
File 3: ValidateAddedValue(...)0%660%
File 3: OnCollectionModifying()0%220%
File 3: OnCollectionModified()0%220%
File 3: GetOrCreateSingleton(...)0%440%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Caching.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.Concurrent;
 5using System.Collections.Generic;
 6using System.Diagnostics;
 7using System.Diagnostics.CodeAnalysis;
 8using System.Runtime.CompilerServices;
 9using System.Runtime.ExceptionServices;
 10using System.Text.Json.Serialization;
 11using System.Text.Json.Serialization.Metadata;
 12using System.Threading;
 13
 14namespace System.Text.Json
 15{
 16    public sealed partial class JsonSerializerOptions
 17    {
 18        /// <summary>
 19        /// Encapsulates all cached metadata referenced by the current <see cref="JsonSerializerOptions" /> instance.
 20        /// Context can be shared across multiple equivalent options instances.
 21        /// </summary>
 22        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 23        internal CachingContext CacheContext
 24        {
 25            get
 026            {
 027                Debug.Assert(IsReadOnly);
 028                return _cachingContext ?? GetOrCreate();
 29
 30                CachingContext GetOrCreate()
 031                {
 032                    CachingContext ctx = TrackedCachingContexts.GetOrCreate(this);
 033                    return Interlocked.CompareExchange(ref _cachingContext, ctx, null) ?? ctx;
 034                }
 035            }
 36        }
 37
 38        private CachingContext? _cachingContext;
 39
 40        // Simple LRU cache for the public (de)serialize entry points that avoid some lookups in _cachingContext.
 41        private volatile JsonTypeInfo? _lastTypeInfo;
 42
 43        /// <summary>
 44        /// Gets the <see cref="JsonTypeInfo"/> contract metadata resolved by the current <see cref="JsonSerializerOptio
 45        /// </summary>
 46        /// <param name="type">The type to resolve contract metadata for.</param>
 47        /// <returns>The contract metadata resolved for <paramref name="type"/>.</returns>
 48        /// <exception cref="ArgumentNullException"><paramref name="type"/> is <see langword="null"/>.</exception>
 49        /// <exception cref="ArgumentException"><paramref name="type"/> is not valid for serialization.</exception>
 50        /// <remarks>
 51        /// Returned metadata can be downcast to <see cref="JsonTypeInfo{T}"/> and used with the relevant <see cref="Jso
 52        ///
 53        /// If the <see cref="JsonSerializerOptions"/> instance is locked for modification, the method will return a cac
 54        /// </remarks>
 55        public JsonTypeInfo GetTypeInfo(Type type)
 056        {
 057            ArgumentNullException.ThrowIfNull(type);
 58
 059            if (JsonTypeInfo.IsInvalidForSerialization(type))
 060            {
 061                ThrowHelper.ThrowArgumentException_CannotSerializeInvalidType(nameof(type), type, null, null);
 62            }
 63
 064            return GetTypeInfoInternal(type, resolveIfMutable: true);
 065        }
 66
 67        /// <summary>
 68        /// Tries to get the <see cref="JsonTypeInfo"/> contract metadata resolved by the current <see cref="JsonSeriali
 69        /// </summary>
 70        /// <param name="type">The type to resolve contract metadata for.</param>
 71        /// <param name="typeInfo">The resolved contract metadata, or <see langword="null" /> if not contract could be r
 72        /// <returns><see langword="true"/> if a contract for <paramref name="type"/> was found, or <see langword="false
 73        /// <exception cref="ArgumentNullException"><paramref name="type"/> is <see langword="null"/>.</exception>
 74        /// <exception cref="ArgumentException"><paramref name="type"/> is not valid for serialization.</exception>
 75        /// <remarks>
 76        /// Returned metadata can be downcast to <see cref="JsonTypeInfo{T}"/> and used with the relevant <see cref="Jso
 77        ///
 78        /// If the <see cref="JsonSerializerOptions"/> instance is locked for modification, the method will return a cac
 79        /// </remarks>
 80        public bool TryGetTypeInfo(Type type, [NotNullWhen(true)] out JsonTypeInfo? typeInfo)
 081        {
 082            ArgumentNullException.ThrowIfNull(type);
 83
 084            if (JsonTypeInfo.IsInvalidForSerialization(type))
 085            {
 086                ThrowHelper.ThrowArgumentException_CannotSerializeInvalidType(nameof(type), type, null, null);
 87            }
 88
 089            typeInfo = GetTypeInfoInternal(type, ensureNotNull: null, resolveIfMutable: true);
 090            return typeInfo is not null;
 091        }
 92
 93        /// <summary>
 94        /// Gets the <see cref="JsonTypeInfo{T}"/> contract metadata resolved by the current <see cref="JsonSerializerOp
 95        /// </summary>
 96        /// <typeparam name="T">The type to resolve contract metadata for.</typeparam>
 97        /// <returns>The contract metadata resolved for <typeparamref name="T"/>.</returns>
 98        /// <exception cref="ArgumentException"><typeparamref name="T"/> is not valid for serialization.</exception>
 99        /// <remarks>
 100        /// If the <see cref="JsonSerializerOptions"/> instance is locked for modification, the method will return a cac
 101        /// </remarks>
 102        public JsonTypeInfo<T> GetTypeInfo<T>()
 0103        {
 0104            return (JsonTypeInfo<T>)GetTypeInfoInternal(typeof(T), resolveIfMutable: true);
 0105        }
 106
 107        /// <summary>
 108        /// Tries to get the <see cref="JsonTypeInfo{T}"/> contract metadata resolved by the current <see cref="JsonSeri
 109        /// </summary>
 110        /// <typeparam name="T">The type to resolve contract metadata for.</typeparam>
 111        /// <param name="typeInfo">The resolved contract metadata, or <see langword="null" /> if no contract could be re
 112        /// <returns><see langword="true"/> if a contract for <typeparamref name="T"/> was found, or <see langword="fals
 113        /// <exception cref="ArgumentException"><typeparamref name="T"/> is not valid for serialization.</exception>
 114        /// <remarks>
 115        /// If the <see cref="JsonSerializerOptions"/> instance is locked for modification, the method will return a cac
 116        /// </remarks>
 117        public bool TryGetTypeInfo<T>([NotNullWhen(true)] out JsonTypeInfo<T>? typeInfo)
 0118        {
 0119            typeInfo = (JsonTypeInfo<T>?)GetTypeInfoInternal(typeof(T), ensureNotNull: null, resolveIfMutable: true);
 0120            return typeInfo is not null;
 0121        }
 122
 123        /// <summary>
 124        /// Same as GetTypeInfo but without validation and additional knobs.
 125        /// </summary>
 126        [return: NotNullIfNotNull(nameof(ensureNotNull))]
 127        internal JsonTypeInfo? GetTypeInfoInternal(
 128            Type type,
 129            bool ensureConfigured = true,
 130            // We can't assert non-nullability on the basis of boolean parameters,
 131            // so use a nullable representation instead to piggy-back on the NotNullIfNotNull attribute.
 132            bool? ensureNotNull = true,
 133            bool resolveIfMutable = false,
 134            bool fallBackToNearestAncestorType = false)
 0135        {
 0136            Debug.Assert(!fallBackToNearestAncestorType || IsReadOnly, "ancestor resolution should only be invoked in re
 0137            Debug.Assert(ensureNotNull is null or true, "Explicitly passing false will result in invalid result annotati
 138
 0139            JsonTypeInfo? typeInfo = null;
 140
 0141            if (IsReadOnly)
 0142            {
 0143                typeInfo = CacheContext.GetOrAddTypeInfo(type, fallBackToNearestAncestorType);
 0144                if (ensureConfigured)
 0145                {
 0146                    typeInfo?.EnsureConfigured();
 0147                }
 0148            }
 0149            else if (resolveIfMutable)
 0150            {
 0151                typeInfo = GetTypeInfoNoCaching(type);
 0152            }
 153
 0154            if (typeInfo is null && ensureNotNull is true)
 0155            {
 0156                ThrowHelper.ThrowNotSupportedException_NoMetadataForType(type, TypeInfoResolver);
 157            }
 158
 0159            return typeInfo;
 0160        }
 161
 162        internal bool TryGetTypeInfoCached(Type type, [NotNullWhen(true)] out JsonTypeInfo? typeInfo)
 0163        {
 0164            if (_cachingContext is null)
 0165            {
 0166                typeInfo = null;
 0167                return false;
 168            }
 169
 0170            return _cachingContext.TryGetTypeInfo(type, out typeInfo);
 0171        }
 172
 173        /// <summary>
 174        /// Return the TypeInfo for root API calls.
 175        /// This has an LRU cache that is intended only for public API calls that specify the root type.
 176        /// </summary>
 177        internal JsonTypeInfo GetTypeInfoForRootType(Type type, bool fallBackToNearestAncestorType = false)
 0178        {
 0179            JsonTypeInfo? jsonTypeInfo = _lastTypeInfo;
 180
 0181            if (jsonTypeInfo?.Type != type)
 0182            {
 0183                _lastTypeInfo = jsonTypeInfo = GetTypeInfoInternal(type, fallBackToNearestAncestorType: fallBackToNeares
 0184            }
 185
 0186            return jsonTypeInfo;
 0187        }
 188
 189        internal bool TryGetPolymorphicTypeInfoForRootType(object rootValue, [NotNullWhen(true)] out JsonTypeInfo? polym
 0190        {
 0191            Debug.Assert(rootValue is not null);
 192
 0193            Type runtimeType = rootValue.GetType();
 0194            if (runtimeType != typeof(object))
 0195            {
 196                // To determine the contract for an object value:
 197                // 1. Find the JsonTypeInfo for the runtime type with fallback to the nearest ancestor, if not available
 198                // 2. If the resolved type is deriving from a polymorphic type, use the contract of the polymorphic type
 0199                polymorphicTypeInfo = GetTypeInfoForRootType(runtimeType, fallBackToNearestAncestorType: true);
 0200                if (polymorphicTypeInfo.AncestorPolymorphicType is { } ancestorPolymorphicType)
 0201                {
 0202                    polymorphicTypeInfo = ancestorPolymorphicType;
 0203                }
 0204                return true;
 205            }
 206
 0207            polymorphicTypeInfo = null;
 0208            return false;
 0209        }
 210
 211        // Caches the resolved JsonTypeInfo<object> for faster access during root-level object type serialization.
 212        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 213        internal JsonTypeInfo ObjectTypeInfo
 214        {
 215            get
 0216            {
 0217                Debug.Assert(IsReadOnly);
 0218                return _objectTypeInfo ??= GetTypeInfoInternal(typeof(object));
 0219            }
 220        }
 221
 222        private JsonTypeInfo? _objectTypeInfo;
 223
 224        internal void ClearCaches()
 0225        {
 0226            _cachingContext?.Clear();
 0227            _lastTypeInfo = null;
 0228            _objectTypeInfo = null;
 0229        }
 230
 231        /// <summary>
 232        /// Stores and manages all reflection caches for one or more <see cref="JsonSerializerOptions"/> instances.
 233        /// NB the type encapsulates the original options instance and only consults that one when building new types;
 234        /// this is to prevent multiple options instances from leaking into the object graphs of converters which
 235        /// could break user invariants.
 236        /// </summary>
 237        internal sealed class CachingContext
 238        {
 0239            private readonly ConcurrentDictionary<Type, CacheEntry> _cache = new();
 240#if !NET
 241            private readonly Func<Type, CacheEntry> _cacheEntryFactory;
 242#endif
 243
 0244            public CachingContext(JsonSerializerOptions options, int hashCode)
 0245            {
 0246                Options = options;
 0247                HashCode = hashCode;
 248#if !NET
 249                _cacheEntryFactory = type => CreateCacheEntry(type, this);
 250#endif
 0251            }
 252
 0253            public JsonSerializerOptions Options { get; }
 0254            public int HashCode { get; }
 255            // Property only accessed by reflection in testing -- do not remove.
 256            // If changing please ensure that src/ILLink.Descriptors.LibraryBuild.xml is up-to-date.
 0257            public int Count => _cache.Count;
 258
 259            public JsonTypeInfo? GetOrAddTypeInfo(Type type, bool fallBackToNearestAncestorType = false)
 0260            {
 0261                CacheEntry entry = GetOrAddCacheEntry(type);
 0262                return fallBackToNearestAncestorType && !entry.HasResult
 0263                    ? FallBackToNearestAncestor(type, entry)
 0264                    : entry.GetResult();
 0265            }
 266
 267            public bool TryGetTypeInfo(Type type, [NotNullWhen(true)] out JsonTypeInfo? typeInfo)
 0268            {
 0269                _cache.TryGetValue(type, out CacheEntry? entry);
 0270                typeInfo = entry?.TypeInfo;
 0271                return typeInfo is not null;
 0272            }
 273
 274            public void Clear()
 0275            {
 0276                _cache.Clear();
 0277            }
 278
 279            private CacheEntry GetOrAddCacheEntry(Type type)
 0280            {
 281#if NET
 0282                return _cache.GetOrAdd(type, CreateCacheEntry, this);
 283#else
 284                return _cache.GetOrAdd(type, _cacheEntryFactory);
 285#endif
 0286            }
 287
 288            private static CacheEntry CreateCacheEntry(Type type, CachingContext context)
 0289            {
 290                try
 0291                {
 0292                    JsonTypeInfo? typeInfo = context.Options.GetTypeInfoNoCaching(type);
 0293                    return new CacheEntry(typeInfo);
 294                }
 0295                catch (Exception ex)
 0296                {
 0297                    ExceptionDispatchInfo edi = ExceptionDispatchInfo.Capture(ex);
 0298                    return new CacheEntry(edi);
 299                }
 0300            }
 301
 302            private JsonTypeInfo? FallBackToNearestAncestor(Type type, CacheEntry entry)
 0303            {
 0304                Debug.Assert(!entry.HasResult);
 305
 0306                CacheEntry? nearestAncestor = entry.IsNearestAncestorResolved
 0307                    ? entry.NearestAncestor
 0308                    : DetermineNearestAncestor(type, entry);
 309
 0310                return nearestAncestor?.GetResult();
 0311            }
 312
 313            [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
 314                Justification = "We only need to examine the interface types that are supported by the underlying resolv
 315            private CacheEntry? DetermineNearestAncestor(Type type, CacheEntry entry)
 0316            {
 317                // In cases where the underlying TypeInfoResolver returns `null` for a given type,
 318                // this method traverses the hierarchy above the type to determine potential
 319                // ancestors for which the resolver does provide metadata. This can be useful in
 320                // cases where we're using a source generator and are trying to serialize private
 321                // implementations of an interface that is supported by the source generator.
 322                // NB this algorithm runs lazily and unsynchronized *after* the CacheEntry has been looked up
 323                // from the global cache, so care should be taken to avoid potential race conditions.
 324                //
 325                // IMPORTANT: nearest-ancestor resolution should be reserved for weakly-typed serialization.
 326                // Attempting to use it in strongly typed operations or deserialization will invariably
 327                // result in an invalid cast exception, so use with caution.
 328
 0329                Debug.Assert(!entry.HasResult);
 0330                CacheEntry? candidate = null;
 0331                Type? candidateType = null;
 332
 0333                for (Type? current = type.BaseType; current != null; current = current.BaseType)
 0334                {
 0335                    if (current == typeof(object))
 0336                    {
 337                        // Avoid falling back to the contract for object since it's polymorphic
 338                        // and it would try to send us back to the runtime type that isn't supported.
 0339                        break;
 340                    }
 341
 0342                    candidate = GetOrAddCacheEntry(current);
 0343                    if (candidate.HasResult)
 0344                    {
 345                        // We found a type in the class hierarchy that has a contract -- stop looking further up.
 0346                        candidateType = current;
 0347                        break;
 348                    }
 0349                }
 350
 0351                foreach (Type interfaceType in type.GetInterfaces())
 0352                {
 0353                    CacheEntry interfaceEntry = GetOrAddCacheEntry(interfaceType);
 0354                    if (interfaceEntry.HasResult)
 0355                    {
 0356                        if (candidateType != null)
 0357                        {
 0358                            if (interfaceType.IsAssignableFrom(candidateType))
 0359                            {
 360                                // The previous candidate is more derived than the
 361                                // current interface -- keep our previous choice.
 0362                                continue;
 363                            }
 0364                            else if (candidateType.IsAssignableFrom(interfaceType))
 0365                            {
 366                                // The current interface is more derived than the
 367                                // previous candidate -- replace the candidate value.
 0368                            }
 369                            else
 0370                            {
 371                                // We have found two possible ancestors that are not in subtype relationship.
 372                                // This indicates we have encountered a diamond ambiguity -- abort search and record an 
 0373                                NotSupportedException nse = ThrowHelper.GetNotSupportedException_AmbiguousMetadataForTyp
 0374                                candidate = new CacheEntry(ExceptionDispatchInfo.Capture(nse));
 0375                                break;
 376                            }
 0377                        }
 378
 0379                        candidate = interfaceEntry;
 0380                        candidateType = interfaceType;
 0381                    }
 0382                }
 383
 0384                entry.NearestAncestor = candidate;
 0385                entry.IsNearestAncestorResolved = true;
 0386                return candidate;
 0387            }
 388
 389            private sealed class CacheEntry
 390            {
 391                public readonly bool HasResult;
 392                public readonly JsonTypeInfo? TypeInfo;
 393                public readonly ExceptionDispatchInfo? ExceptionDispatchInfo;
 394
 395                public volatile bool IsNearestAncestorResolved;
 396                public CacheEntry? NearestAncestor;
 397
 0398                public CacheEntry(JsonTypeInfo? typeInfo)
 0399                {
 0400                    TypeInfo = typeInfo;
 0401                    HasResult = typeInfo is not null;
 0402                }
 403
 0404                public CacheEntry(ExceptionDispatchInfo exception)
 0405                {
 0406                    ExceptionDispatchInfo = exception;
 0407                    HasResult = true;
 0408                }
 409
 410                public JsonTypeInfo? GetResult()
 0411                {
 0412                    ExceptionDispatchInfo?.Throw();
 0413                    return TypeInfo;
 0414                }
 415            }
 416        }
 417
 418        /// <summary>
 419        /// Defines a cache of CachingContexts; instead of using a ConditionalWeakTable which can be slow to traverse
 420        /// this approach uses a fixed-size array of weak references of <see cref="CachingContext"/> that can be looked 
 421        /// Relevant caching contexts are looked up by linear traversal using the equality comparison defined by <see cr
 422        /// </summary>
 423        internal static class TrackedCachingContexts
 424        {
 425            private const int MaxTrackedContexts = 64;
 0426            private static readonly WeakReference<CachingContext>?[] s_trackedContexts = new WeakReference<CachingContex
 0427            private static readonly EqualityComparer s_optionsComparer = new();
 428
 429            public static CachingContext GetOrCreate(JsonSerializerOptions options)
 0430            {
 0431                Debug.Assert(options.IsReadOnly, "Cannot create caching contexts for mutable JsonSerializerOptions insta
 0432                Debug.Assert(options._typeInfoResolver is not null);
 433
 0434                int hashCode = s_optionsComparer.GetHashCode(options);
 435
 0436                if (TryGetContext(options, hashCode, out int firstUnpopulatedIndex, out CachingContext? result))
 0437                {
 0438                    return result;
 439                }
 0440                else if (firstUnpopulatedIndex < 0)
 0441                {
 442                    // Cache is full; return a fresh instance.
 0443                    return new CachingContext(options, hashCode);
 444                }
 445
 0446                lock (s_trackedContexts)
 0447                {
 0448                    if (TryGetContext(options, hashCode, out firstUnpopulatedIndex, out result))
 0449                    {
 0450                        return result;
 451                    }
 452
 0453                    var ctx = new CachingContext(options, hashCode);
 454
 0455                    if (firstUnpopulatedIndex >= 0)
 0456                    {
 457                        // Cache has capacity -- store the context in the first available index.
 0458                        ref WeakReference<CachingContext>? weakRef = ref s_trackedContexts[firstUnpopulatedIndex];
 459
 0460                        if (weakRef is null)
 0461                        {
 0462                            weakRef = new(ctx);
 0463                        }
 464                        else
 0465                        {
 0466                            Debug.Assert(!weakRef.TryGetTarget(out _));
 0467                            weakRef.SetTarget(ctx);
 0468                        }
 0469                    }
 470
 0471                    return ctx;
 472                }
 0473            }
 474
 475            private static bool TryGetContext(
 476                JsonSerializerOptions options,
 477                int hashCode,
 478                out int firstUnpopulatedIndex,
 479                [NotNullWhen(true)] out CachingContext? result)
 0480            {
 0481                WeakReference<CachingContext>?[] trackedContexts = s_trackedContexts;
 482
 0483                firstUnpopulatedIndex = -1;
 0484                for (int i = 0; i < trackedContexts.Length; i++)
 0485                {
 0486                    WeakReference<CachingContext>? weakRef = trackedContexts[i];
 487
 0488                    if (weakRef is null || !weakRef.TryGetTarget(out CachingContext? ctx))
 0489                    {
 0490                        if (firstUnpopulatedIndex < 0)
 0491                        {
 0492                            firstUnpopulatedIndex = i;
 0493                        }
 0494                    }
 0495                    else if (hashCode == ctx.HashCode && s_optionsComparer.Equals(options, ctx.Options))
 0496                    {
 0497                        result = ctx;
 0498                        return true;
 499                    }
 0500                }
 501
 0502                result = null;
 0503                return false;
 0504            }
 505        }
 506
 507        /// <summary>
 508        /// Provides a conservative equality comparison for JsonSerializerOptions instances.
 509        /// If two instances are equivalent, they should generate identical metadata caches;
 510        /// the converse however does not necessarily hold.
 511        /// </summary>
 512        private sealed class EqualityComparer : IEqualityComparer<JsonSerializerOptions>
 513        {
 514            public bool Equals(JsonSerializerOptions? left, JsonSerializerOptions? right)
 0515            {
 0516                Debug.Assert(left is not null && right is not null);
 517
 0518                return
 0519                    left._dictionaryKeyPolicy == right._dictionaryKeyPolicy &&
 0520                    left._jsonPropertyNamingPolicy == right._jsonPropertyNamingPolicy &&
 0521                    left._readCommentHandling == right._readCommentHandling &&
 0522                    left._referenceHandler == right._referenceHandler &&
 0523                    left._encoder == right._encoder &&
 0524                    left._defaultIgnoreCondition == right._defaultIgnoreCondition &&
 0525                    left._numberHandling == right._numberHandling &&
 0526                    left._preferredObjectCreationHandling == right._preferredObjectCreationHandling &&
 0527                    left._unknownTypeHandling == right._unknownTypeHandling &&
 0528                    left._unmappedMemberHandling == right._unmappedMemberHandling &&
 0529                    left._defaultBufferSize == right._defaultBufferSize &&
 0530                    left._maxDepth == right._maxDepth &&
 0531                    left.NewLine == right.NewLine && // Read through property due to lazy initialization of the backing 
 0532                    left._allowOutOfOrderMetadataProperties == right._allowOutOfOrderMetadataProperties &&
 0533                    left._allowTrailingCommas == right._allowTrailingCommas &&
 0534                    left._respectNullableAnnotations == right._respectNullableAnnotations &&
 0535                    left._respectRequiredConstructorParameters == right._respectRequiredConstructorParameters &&
 0536                    left._ignoreNullValues == right._ignoreNullValues &&
 0537                    left._ignoreReadOnlyProperties == right._ignoreReadOnlyProperties &&
 0538                    left._ignoreReadonlyFields == right._ignoreReadonlyFields &&
 0539                    left._includeFields == right._includeFields &&
 0540                    left._propertyNameCaseInsensitive == right._propertyNameCaseInsensitive &&
 0541                    left._writeIndented == right._writeIndented &&
 0542                    left._indentCharacter == right._indentCharacter &&
 0543                    left._indentSize == right._indentSize &&
 0544                    left._typeInfoResolver == right._typeInfoResolver &&
 0545                    left._allowDuplicateProperties == right._allowDuplicateProperties &&
 0546                    left._inferClosedTypePolymorphism == right._inferClosedTypePolymorphism &&
 0547                    CompareLists(left._converters, right._converters) &&
 0548                    CompareLists(left._typeClassifiers, right._typeClassifiers);
 549
 550                static bool CompareLists<TValue>(ConfigurationList<TValue>? left, ConfigurationList<TValue>? right)
 551                    where TValue : class?
 0552                {
 553                    // equates null with empty lists
 0554                    if (left is null)
 0555                        return right is null || right.Count == 0;
 556
 0557                    if (right is null)
 0558                        return left.Count == 0;
 559
 560                    int n;
 0561                    if ((n = left.Count) != right.Count)
 0562                    {
 0563                        return false;
 564                    }
 565
 0566                    for (int i = 0; i < n; i++)
 0567                    {
 0568                        if (left[i] != right[i])
 0569                        {
 0570                            return false;
 571                        }
 0572                    }
 573
 0574                    return true;
 0575                }
 0576            }
 577
 578            public int GetHashCode(JsonSerializerOptions options)
 0579            {
 0580                HashCode hc = default;
 581
 0582                AddHashCode(ref hc, options._dictionaryKeyPolicy);
 0583                AddHashCode(ref hc, options._jsonPropertyNamingPolicy);
 0584                AddHashCode(ref hc, options._readCommentHandling);
 0585                AddHashCode(ref hc, options._referenceHandler);
 0586                AddHashCode(ref hc, options._encoder);
 0587                AddHashCode(ref hc, options._defaultIgnoreCondition);
 0588                AddHashCode(ref hc, options._numberHandling);
 0589                AddHashCode(ref hc, options._preferredObjectCreationHandling);
 0590                AddHashCode(ref hc, options._unknownTypeHandling);
 0591                AddHashCode(ref hc, options._unmappedMemberHandling);
 0592                AddHashCode(ref hc, options._defaultBufferSize);
 0593                AddHashCode(ref hc, options._maxDepth);
 0594                AddHashCode(ref hc, options.NewLine); // Read through property due to lazy initialization of the backing
 0595                AddHashCode(ref hc, options._allowOutOfOrderMetadataProperties);
 0596                AddHashCode(ref hc, options._allowTrailingCommas);
 0597                AddHashCode(ref hc, options._respectNullableAnnotations);
 0598                AddHashCode(ref hc, options._respectRequiredConstructorParameters);
 0599                AddHashCode(ref hc, options._ignoreNullValues);
 0600                AddHashCode(ref hc, options._ignoreReadOnlyProperties);
 0601                AddHashCode(ref hc, options._ignoreReadonlyFields);
 0602                AddHashCode(ref hc, options._includeFields);
 0603                AddHashCode(ref hc, options._propertyNameCaseInsensitive);
 0604                AddHashCode(ref hc, options._writeIndented);
 0605                AddHashCode(ref hc, options._indentCharacter);
 0606                AddHashCode(ref hc, options._indentSize);
 0607                AddHashCode(ref hc, options._typeInfoResolver);
 0608                AddHashCode(ref hc, options._allowDuplicateProperties);
 0609                AddHashCode(ref hc, options._inferClosedTypePolymorphism);
 0610                AddListHashCode(ref hc, options._converters);
 0611                AddListHashCode(ref hc, options._typeClassifiers);
 612
 0613                return hc.ToHashCode();
 614
 615                static void AddListHashCode<TValue>(ref HashCode hc, ConfigurationList<TValue>? list)
 0616                {
 617                    // equates null with empty lists
 0618                    if (list is null)
 0619                        return;
 620
 0621                    int n = list.Count;
 0622                    for (int i = 0; i < n; i++)
 0623                    {
 0624                        AddHashCode(ref hc, list[i]);
 0625                    }
 0626                }
 627
 628                static void AddHashCode<TValue>(ref HashCode hc, TValue? value)
 0629                {
 0630                    if (typeof(TValue).IsSealed)
 0631                    {
 0632                        hc.Add(value);
 0633                    }
 634                    else
 0635                    {
 636                        // Use the built-in hashcode for types that could be overriding GetHashCode().
 0637                        hc.Add(RuntimeHelpers.GetHashCode(value));
 0638                    }
 0639                }
 0640            }
 641
 642#if !NET
 643            /// <summary>
 644            /// Polyfill for System.HashCode.
 645            /// </summary>
 646            private struct HashCode
 647            {
 648                private int _hashCode;
 649                public void Add<T>(T? value) => _hashCode = (_hashCode, value).GetHashCode();
 650                public int ToHashCode() => _hashCode;
 651            }
 652#endif
 653        }
 654    }
 655}
 656

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.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.CodeAnalysis;
 6using System.Text.Json.Reflection;
 7using System.Text.Json.Serialization;
 8using System.Text.Json.Serialization.Metadata;
 9
 10namespace System.Text.Json
 11{
 12    /// <summary>
 13    /// Provides options to be used with <see cref="JsonSerializer"/>.
 14    /// </summary>
 15    public sealed partial class JsonSerializerOptions
 16    {
 17        /// <summary>
 18        /// The list of custom converters.
 19        /// </summary>
 20        /// <remarks>
 21        /// Once serialization or deserialization occurs, the list cannot be modified.
 22        /// </remarks>
 023        public IList<JsonConverter> Converters => _converters ??= new(this);
 24
 25        /// <summary>
 26        /// Gets the list of custom type classifier factories.
 27        /// </summary>
 28        /// <remarks>
 29        /// <para>
 30        /// Each <see cref="JsonTypeClassifierFactory"/> in the list is consulted (in declaration order) when
 31        /// configuring a union or polymorphic type for which no per-type classifier was set via
 32        /// <see cref="JsonUnionAttribute.TypeClassifier"/> or <see cref="JsonPolymorphicAttribute.TypeClassifier"/>.
 33        /// The first factory whose
 34        /// <see cref="JsonTypeClassifierFactory.CanClassify(JsonTypeClassifierContext)"/> returns <see langword="true"/
 35        /// is used; otherwise the built-in type resolution for that metadata shape applies.
 36        /// </para>
 37        /// <para>
 38        /// Once serialization or deserialization occurs, the list cannot be modified.
 39        /// </para>
 40        /// </remarks>
 041        public IList<JsonTypeClassifierFactory> TypeClassifiers => _typeClassifiers ??= new(this);
 42
 43        /// <summary>
 44        /// Returns the converter for the specified type.
 45        /// </summary>
 46        /// <param name="typeToConvert">The type to return a converter for.</param>
 47        /// <returns>
 48        /// The converter for the given type.
 49        /// </returns>
 50        /// <exception cref="InvalidOperationException">
 51        /// The configured <see cref="JsonConverter"/> for <paramref name="typeToConvert"/> returned an invalid converte
 52        /// </exception>
 53        /// <exception cref="NotSupportedException">
 54        /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/>
 55        /// for <paramref name="typeToConvert"/> or its serializable members.
 56        /// </exception>
 57        [RequiresUnreferencedCode("Getting a converter for a type may require reflection which depends on unreferenced c
 58        [RequiresDynamicCode("Getting a converter for a type may require reflection which depends on runtime code genera
 59        public JsonConverter GetConverter(Type typeToConvert)
 060        {
 061            ArgumentNullException.ThrowIfNull(typeToConvert);
 62
 063            if (JsonSerializer.IsReflectionEnabledByDefault)
 064            {
 65                // Backward compatibility -- root & query the default reflection converters
 66                // but do not populate the TypeInfoResolver setting.
 067                if (_typeInfoResolver is null)
 068                {
 069                    return DefaultJsonTypeInfoResolver.GetConverterForType(typeToConvert, this);
 70                }
 071            }
 72
 073            return GetConverterInternal(typeToConvert);
 074        }
 75
 76        /// <summary>
 77        /// Same as GetConverter but without defaulting to reflection converters.
 78        /// </summary>
 79        internal JsonConverter GetConverterInternal(Type typeToConvert)
 080        {
 081            JsonTypeInfo jsonTypeInfo = GetTypeInfoInternal(typeToConvert, ensureConfigured: false, resolveIfMutable: tr
 082            return jsonTypeInfo.Converter;
 083        }
 84
 85        internal JsonConverter? GetConverterFromList(Type typeToConvert)
 086        {
 087            if (_converters is { } converterList)
 088            {
 089                foreach (JsonConverter item in converterList)
 090                {
 091                    if (item.CanConvert(typeToConvert))
 092                    {
 093                        return item;
 94                    }
 095                }
 096            }
 97
 098            return null;
 099        }
 100
 101        internal JsonTypeClassifierFactory? GetTypeClassifierFromList(JsonTypeClassifierContext context)
 0102        {
 0103            if (_typeClassifiers is { } classifierList)
 0104            {
 0105                foreach (JsonTypeClassifierFactory item in classifierList)
 0106                {
 0107                    if (item.CanClassify(context))
 0108                    {
 0109                        return item;
 110                    }
 0111                }
 0112            }
 113
 0114            return null;
 0115        }
 116
 117        [return: NotNullIfNotNull(nameof(converter))]
 118        internal JsonConverter? ExpandConverterFactory(JsonConverter? converter, Type typeToConvert)
 0119        {
 0120            if (converter is JsonConverterFactory factory)
 0121            {
 0122                converter = factory.GetConverterInternal(typeToConvert, this);
 0123            }
 124
 0125            return converter;
 0126        }
 127
 128        internal static void CheckConverterNullabilityIsSameAsPropertyType(JsonConverter converter, Type propertyType)
 0129        {
 130            // User has indicated that either:
 131            //   a) a non-nullable-struct handling converter should handle a nullable struct type or
 132            //   b) a nullable-struct handling converter should handle a non-nullable struct type.
 133            // User should implement a custom converter for the underlying struct and remove the unnecessary CanConvert 
 134            // The serializer will automatically wrap the custom converter with NullableConverter<T>.
 135            //
 136            // We also throw to avoid passing an invalid argument to setters for nullable struct properties,
 137            // which would cause an InvalidProgramException when the generated IL is invoked.
 0138            if (propertyType.IsValueType && converter.IsValueType &&
 0139                (propertyType.IsNullableOfT() ^ converter.Type!.IsNullableOfT()))
 0140            {
 0141                ThrowHelper.ThrowInvalidOperationException_ConverterCanConvertMultipleTypes(propertyType, converter);
 142            }
 0143        }
 144    }
 145}
 146

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.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.ComponentModel;
 6using System.Diagnostics;
 7using System.Diagnostics.CodeAnalysis;
 8using System.Runtime.CompilerServices;
 9using System.Text.Encodings.Web;
 10using System.Text.Json.Nodes;
 11using System.Text.Json.Serialization;
 12using System.Text.Json.Serialization.Converters;
 13using System.Text.Json.Serialization.Metadata;
 14using System.Threading;
 15
 16namespace System.Text.Json
 17{
 18    /// <summary>
 19    /// Provides options to be used with <see cref="JsonSerializer"/>.
 20    /// </summary>
 21    [DebuggerDisplay("{DebuggerDisplay,nq}")]
 22    public sealed partial class JsonSerializerOptions
 23    {
 24        internal const int BufferSizeDefault = 16 * 1024;
 25
 26        // For backward compatibility the default max depth for JsonSerializer is 64,
 27        // the minimum of JsonReaderOptions.DefaultMaxDepth and JsonWriterOptions.DefaultMaxDepth.
 28        internal const int DefaultMaxDepth = JsonReaderOptions.DefaultMaxDepth;
 29
 30        /// <summary>
 31        /// Gets a read-only, singleton instance of <see cref="JsonSerializerOptions" /> that uses the default configura
 32        /// </summary>
 33        /// <remarks>
 34        /// Each <see cref="JsonSerializerOptions" /> instance encapsulates its own serialization metadata caches,
 35        /// so using fresh default instances every time one is needed can result in redundant recomputation of converter
 36        /// This property provides a shared instance that can be consumed by any number of components without necessitat
 37        /// </remarks>
 38        public static JsonSerializerOptions Default
 39        {
 40            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 41            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 042            get => field ?? GetOrCreateSingleton(ref field, JsonSerializerDefaults.General);
 43        }
 44
 45        /// <summary>
 46        /// Gets a read-only, singleton instance of <see cref="JsonSerializerOptions" /> that uses the web configuration
 47        /// </summary>
 48        /// <remarks>
 49        /// Each <see cref="JsonSerializerOptions" /> instance encapsulates its own serialization metadata caches,
 50        /// so using fresh default instances every time one is needed can result in redundant recomputation of converter
 51        /// This property provides a shared instance that can be consumed by any number of components without necessitat
 52        /// </remarks>
 53        public static JsonSerializerOptions Web
 54        {
 55            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 56            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 057            get => field ?? GetOrCreateSingleton(ref field, JsonSerializerDefaults.Web);
 58        }
 59
 60        /// <summary>
 61        /// Gets a read-only, singleton instance of <see cref="JsonSerializerOptions" /> that uses the strict configurat
 62        /// </summary>
 63        /// <remarks>
 64        /// Each <see cref="JsonSerializerOptions" /> instance encapsulates its own serialization metadata caches,
 65        /// so using fresh default instances every time one is needed can result in redundant recomputation of converter
 66        /// This property provides a shared instance that can be consumed by any number of components without necessitat
 67        /// </remarks>
 68        public static JsonSerializerOptions Strict
 69        {
 70            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 71            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 072            get => field ?? GetOrCreateSingleton(ref field, JsonSerializerDefaults.Strict);
 73        }
 74
 75        // For any new option added, consider adding it to the options copied in the copy constructor below
 76        // and consider updating the EqualtyComparer used for comparing CachingContexts.
 77        private IJsonTypeInfoResolver? _typeInfoResolver;
 78        private JsonNamingPolicy? _dictionaryKeyPolicy;
 79        private JsonNamingPolicy? _jsonPropertyNamingPolicy;
 80        private JsonCommentHandling _readCommentHandling;
 81        private ReferenceHandler? _referenceHandler;
 82        private JavaScriptEncoder? _encoder;
 83        private ConverterList? _converters;
 84        private TypeClassifierList? _typeClassifiers;
 85        private JsonIgnoreCondition _defaultIgnoreCondition;
 86        private JsonNumberHandling _numberHandling;
 87        private JsonObjectCreationHandling _preferredObjectCreationHandling;
 88        private JsonUnknownTypeHandling _unknownTypeHandling;
 89        private JsonUnmappedMemberHandling _unmappedMemberHandling;
 90
 091        private int _defaultBufferSize = BufferSizeDefault;
 92        private int _maxDepth;
 93        private bool _allowOutOfOrderMetadataProperties;
 94        private bool _allowTrailingCommas;
 095        private bool _respectNullableAnnotations = AppContextSwitchHelper.RespectNullableAnnotationsDefault;
 096        private bool _respectRequiredConstructorParameters = AppContextSwitchHelper.RespectRequiredConstructorParameters
 97        private bool _ignoreNullValues;
 98        private bool _ignoreReadOnlyProperties;
 99        private bool _ignoreReadonlyFields;
 100        private bool _includeFields;
 101        private string? _newLine;
 102        private bool _propertyNameCaseInsensitive;
 103        private bool _writeIndented;
 0104        private char _indentCharacter = JsonConstants.DefaultIndentCharacter;
 0105        private int _indentSize = JsonConstants.DefaultIndentSize;
 0106        private bool _allowDuplicateProperties = true;
 107        private bool _inferClosedTypePolymorphism;
 108
 109        /// <summary>
 110        /// Constructs a new <see cref="JsonSerializerOptions"/> instance.
 111        /// </summary>
 0112        public JsonSerializerOptions()
 0113        {
 0114            TrackOptionsInstance(this);
 0115        }
 116
 117        /// <summary>
 118        /// Copies the options from a <see cref="JsonSerializerOptions"/> instance to a new instance.
 119        /// </summary>
 120        /// <param name="options">The <see cref="JsonSerializerOptions"/> instance to copy options from.</param>
 121        /// <exception cref="System.ArgumentNullException">
 122        /// <paramref name="options"/> is <see langword="null"/>.
 123        /// </exception>
 0124        public JsonSerializerOptions(JsonSerializerOptions options)
 0125        {
 0126            ArgumentNullException.ThrowIfNull(options);
 127
 128            // The following fields are not copied intentionally:
 129            // 1. _cachingContext can only be set in immutable options instances.
 130            // 2. _typeInfoResolverChain can be created lazily as it relies on
 131            //    _typeInfoResolver as its source of truth.
 132
 0133            _dictionaryKeyPolicy = options._dictionaryKeyPolicy;
 0134            _jsonPropertyNamingPolicy = options._jsonPropertyNamingPolicy;
 0135            _readCommentHandling = options._readCommentHandling;
 0136            _referenceHandler = options._referenceHandler;
 0137            _converters = options._converters is { } converters ? new(this, converters) : null;
 0138            _typeClassifiers = options._typeClassifiers is { } TypeClassifiers ? new(this, TypeClassifiers) : null;
 0139            _encoder = options._encoder;
 0140            _defaultIgnoreCondition = options._defaultIgnoreCondition;
 0141            _numberHandling = options._numberHandling;
 0142            _preferredObjectCreationHandling = options._preferredObjectCreationHandling;
 0143            _unknownTypeHandling = options._unknownTypeHandling;
 0144            _unmappedMemberHandling = options._unmappedMemberHandling;
 145
 0146            _defaultBufferSize = options._defaultBufferSize;
 0147            _maxDepth = options._maxDepth;
 0148            _allowOutOfOrderMetadataProperties = options._allowOutOfOrderMetadataProperties;
 0149            _allowTrailingCommas = options._allowTrailingCommas;
 0150            _respectNullableAnnotations = options._respectNullableAnnotations;
 0151            _respectRequiredConstructorParameters = options._respectRequiredConstructorParameters;
 0152            _ignoreNullValues = options._ignoreNullValues;
 0153            _ignoreReadOnlyProperties = options._ignoreReadOnlyProperties;
 0154            _ignoreReadonlyFields = options._ignoreReadonlyFields;
 0155            _includeFields = options._includeFields;
 0156            _newLine = options._newLine;
 0157            _propertyNameCaseInsensitive = options._propertyNameCaseInsensitive;
 0158            _writeIndented = options._writeIndented;
 0159            _indentCharacter = options._indentCharacter;
 0160            _indentSize = options._indentSize;
 0161            _allowDuplicateProperties = options._allowDuplicateProperties;
 0162            _inferClosedTypePolymorphism = options._inferClosedTypePolymorphism;
 0163            _typeInfoResolver = options._typeInfoResolver;
 0164            EffectiveMaxDepth = options.EffectiveMaxDepth;
 0165            ReferenceHandlingStrategy = options.ReferenceHandlingStrategy;
 166
 0167            TrackOptionsInstance(this);
 0168        }
 169
 170        /// <summary>
 171        /// Constructs a new <see cref="JsonSerializerOptions"/> instance with a predefined set of options determined by
 172        /// </summary>
 173        /// <param name="defaults"> The <see cref="JsonSerializerDefaults"/> to reason about.</param>
 0174        public JsonSerializerOptions(JsonSerializerDefaults defaults) : this()
 0175        {
 176            // Should be kept in sync with equivalent overload in JsonSourceGenerationOptionsAttribute
 177
 0178            if (defaults == JsonSerializerDefaults.Web)
 0179            {
 0180                _propertyNameCaseInsensitive = true;
 0181                _jsonPropertyNamingPolicy = JsonNamingPolicy.CamelCase;
 0182                _numberHandling = JsonNumberHandling.AllowReadingFromString;
 0183            }
 0184            else if (defaults == JsonSerializerDefaults.Strict)
 0185            {
 0186                _unmappedMemberHandling = JsonUnmappedMemberHandling.Disallow;
 0187                _allowDuplicateProperties = false;
 0188                _respectNullableAnnotations = true;
 0189                _respectRequiredConstructorParameters = true;
 0190            }
 0191            else if (defaults != JsonSerializerDefaults.General)
 0192            {
 0193                throw new ArgumentOutOfRangeException(nameof(defaults));
 194            }
 0195        }
 196
 197        /// <summary>Tracks the options instance to enable all instances to be enumerated.</summary>
 0198        private static void TrackOptionsInstance(JsonSerializerOptions options) => TrackedOptionsInstances.All.Add(optio
 199
 200        internal static class TrackedOptionsInstances
 201        {
 202            /// <summary>Tracks all live JsonSerializerOptions instances.</summary>
 203            /// <remarks>Instances are added to the table in their constructor.</remarks>
 0204            public static ConditionalWeakTable<JsonSerializerOptions, object?> All { get; } =
 205                // TODO https://github.com/dotnet/runtime/issues/51159:
 206                // Look into linking this away / disabling it when hot reload isn't in use.
 0207                new ConditionalWeakTable<JsonSerializerOptions, object?>();
 208        }
 209
 210        /// <summary>
 211        /// Binds current <see cref="JsonSerializerOptions"/> instance with a new instance of the specified <see cref="S
 212        /// </summary>
 213        /// <typeparam name="TContext">The generic definition of the specified context type.</typeparam>
 214        /// <remarks>
 215        /// When serializing and deserializing types using the options
 216        /// instance, metadata for the types will be fetched from the context instance.
 217        /// </remarks>
 218        [Obsolete(Obsoletions.JsonSerializerOptionsAddContextMessage, DiagnosticId = Obsoletions.JsonSerializerOptionsAd
 219        [EditorBrowsable(EditorBrowsableState.Never)]
 220        public void AddContext<TContext>() where TContext : JsonSerializerContext, new()
 0221        {
 0222            VerifyMutable();
 0223            TContext context = new();
 0224            context.AssociateWithOptions(this);
 0225        }
 226
 227        /// <summary>
 228        /// Gets or sets the <see cref="JsonTypeInfo"/> contract resolver used by this instance.
 229        /// </summary>
 230        /// <exception cref="InvalidOperationException">
 231        /// Thrown if this property is set after serialization or deserialization has occurred.
 232        /// </exception>
 233        /// <remarks>
 234        /// A <see langword="null"/> setting is equivalent to using the reflection-based <see cref="DefaultJsonTypeInfoR
 235        /// The property will be populated automatically once used with one of the <see cref="JsonSerializer"/> methods.
 236        ///
 237        /// This property is kept in sync with the <see cref="TypeInfoResolverChain"/> property.
 238        /// Any change made to this property will be reflected by <see cref="TypeInfoResolverChain"/> and vice versa.
 239        /// </remarks>
 240        public IJsonTypeInfoResolver? TypeInfoResolver
 241        {
 242            get
 0243            {
 0244                return _typeInfoResolver;
 0245            }
 246            set
 0247            {
 0248                VerifyMutable();
 249
 0250                if (_typeInfoResolverChain is { } resolverChain && !ReferenceEquals(resolverChain, value))
 0251                {
 252                    // User is setting a new resolver; detach the resolver chain if already created.
 0253                    resolverChain.DetachFromOptions();
 0254                    _typeInfoResolverChain = null;
 0255                }
 256
 0257                _typeInfoResolver = value;
 0258            }
 259        }
 260
 261        /// <summary>
 262        /// Gets the list of chained <see cref="JsonTypeInfo"/> contract resolvers used by this instance.
 263        /// </summary>
 264        /// <remarks>
 265        /// The ordering of the chain is significant: <see cref="JsonSerializerOptions "/> will query each
 266        /// of the resolvers in their specified order, returning the first result that is non-null.
 267        /// If all resolvers in the chain return null, then <see cref="JsonSerializerOptions"/> will also return null.
 268        ///
 269        /// This property is auxiliary to and is kept in sync with the <see cref="TypeInfoResolver"/> property.
 270        /// Any change made to this property will be reflected by <see cref="TypeInfoResolver"/> and vice versa.
 271        /// </remarks>
 0272        public IList<IJsonTypeInfoResolver> TypeInfoResolverChain => _typeInfoResolverChain ??= new(this);
 273        private OptionsBoundJsonTypeInfoResolverChain? _typeInfoResolverChain;
 274
 275        /// <summary>
 276        /// Allows JSON metadata properties to be specified after regular properties in a deserialized JSON object.
 277        /// </summary>
 278        /// <exception cref="InvalidOperationException">
 279        /// Thrown if this property is set after serialization or deserialization has occurred.
 280        /// </exception>
 281        /// <remarks>
 282        /// When set to <see langword="true" />, removes the requirement that JSON metadata properties
 283        /// such as $id and $type should be specified at the very start of the deserialized JSON object.
 284        ///
 285        /// It should be noted that enabling this setting can result in over-buffering
 286        /// when deserializing large JSON payloads in the context of streaming deserialization.
 287        /// </remarks>
 288        public bool AllowOutOfOrderMetadataProperties
 289        {
 290            get
 0291            {
 0292                return _allowOutOfOrderMetadataProperties;
 0293            }
 294            set
 0295            {
 0296                VerifyMutable();
 0297                _allowOutOfOrderMetadataProperties = value;
 0298            }
 299        }
 300
 301        /// <summary>
 302        /// Defines whether an extra comma at the end of a list of JSON values in an object or array
 303        /// is allowed (and ignored) within the JSON payload being deserialized.
 304        /// </summary>
 305        /// <exception cref="InvalidOperationException">
 306        /// Thrown if this property is set after serialization or deserialization has occurred.
 307        /// </exception>
 308        /// <remarks>
 309        /// By default, it's set to false, and <exception cref="JsonException"/> is thrown if a trailing comma is encoun
 310        /// </remarks>
 311        public bool AllowTrailingCommas
 312        {
 313            get
 0314            {
 0315                return _allowTrailingCommas;
 0316            }
 317            set
 0318            {
 0319                VerifyMutable();
 0320                _allowTrailingCommas = value;
 0321            }
 322        }
 323
 324        /// <summary>
 325        /// The default buffer size in bytes used when creating temporary buffers.
 326        /// </summary>
 327        /// <remarks>The default size is 16K.</remarks>
 328        /// <exception cref="System.ArgumentException">Thrown when the buffer size is less than 1.</exception>
 329        /// <exception cref="InvalidOperationException">
 330        /// Thrown if this property is set after serialization or deserialization has occurred.
 331        /// </exception>
 332        public int DefaultBufferSize
 333        {
 334            get
 0335            {
 0336                return _defaultBufferSize;
 0337            }
 338            set
 0339            {
 0340                VerifyMutable();
 341
 0342                if (value < 1)
 0343                {
 0344                    throw new ArgumentException(SR.SerializationInvalidBufferSize);
 345                }
 346
 0347                _defaultBufferSize = value;
 0348            }
 349        }
 350
 351        /// <summary>
 352        /// The encoder to use when escaping strings, or <see langword="null" /> to use the default encoder.
 353        /// </summary>
 354        public JavaScriptEncoder? Encoder
 355        {
 356            get
 0357            {
 0358                return _encoder;
 0359            }
 360            set
 0361            {
 0362                VerifyMutable();
 363
 0364                _encoder = value;
 0365            }
 366        }
 367
 368        /// <summary>
 369        /// Specifies the policy used to convert a <see cref="System.Collections.IDictionary"/> key's name to another fo
 370        /// </summary>
 371        /// <remarks>
 372        /// This property can be set to <see cref="JsonNamingPolicy.CamelCase"/> to specify a camel-casing policy.
 373        /// It is not used when deserializing.
 374        /// </remarks>
 375        public JsonNamingPolicy? DictionaryKeyPolicy
 376        {
 377            get
 0378            {
 0379                return _dictionaryKeyPolicy;
 0380            }
 381            set
 0382            {
 0383                VerifyMutable();
 0384                _dictionaryKeyPolicy = value;
 0385            }
 386        }
 387
 388        /// <summary>
 389        /// Determines whether null values are ignored during serialization and deserialization.
 390        /// The default value is false.
 391        /// </summary>
 392        /// <exception cref="InvalidOperationException">
 393        /// Thrown if this property is set after serialization or deserialization has occurred.
 394        /// or <see cref="DefaultIgnoreCondition"/> has been set to a non-default value. These properties cannot be used
 395        /// </exception>
 396        [Obsolete(Obsoletions.JsonSerializerOptionsIgnoreNullValuesMessage, DiagnosticId = Obsoletions.JsonSerializerOpt
 397        [EditorBrowsable(EditorBrowsableState.Never)]
 398        public bool IgnoreNullValues
 399        {
 400            get
 0401            {
 0402                return _ignoreNullValues;
 0403            }
 404            set
 0405            {
 0406                VerifyMutable();
 407
 0408                if (value && _defaultIgnoreCondition != JsonIgnoreCondition.Never)
 0409                {
 0410                    throw new InvalidOperationException(SR.DefaultIgnoreConditionAlreadySpecified);
 411                }
 412
 0413                _ignoreNullValues = value;
 0414            }
 415        }
 416
 417        /// <summary>
 418        /// Specifies a condition to determine when properties with default values are ignored during serialization or d
 419        /// The default value is <see cref="JsonIgnoreCondition.Never" />.
 420        /// </summary>
 421        /// <exception cref="ArgumentException">
 422        /// Thrown if this property is set to <see cref="JsonIgnoreCondition.Always"/>.
 423        /// </exception>
 424        /// <exception cref="InvalidOperationException">
 425        /// Thrown if this property is set after serialization or deserialization has occurred,
 426        /// or <see cref="IgnoreNullValues"/> has been set to <see langword="true"/>. These properties cannot be used to
 427        /// </exception>
 428        public JsonIgnoreCondition DefaultIgnoreCondition
 429        {
 430            get
 0431            {
 0432                return _defaultIgnoreCondition;
 0433            }
 434            set
 0435            {
 0436                VerifyMutable();
 437
 0438                if (value == JsonIgnoreCondition.Always)
 0439                {
 0440                    throw new ArgumentException(SR.DefaultIgnoreConditionInvalid);
 441                }
 442
 0443                if (value != JsonIgnoreCondition.Never && _ignoreNullValues)
 0444                {
 0445                    throw new InvalidOperationException(SR.DefaultIgnoreConditionAlreadySpecified);
 446                }
 447
 0448                _defaultIgnoreCondition = value;
 0449            }
 450        }
 451
 452        /// <summary>
 453        /// Specifies how number types should be handled when serializing or deserializing.
 454        /// </summary>
 455        /// <exception cref="InvalidOperationException">
 456        /// Thrown if this property is set after serialization or deserialization has occurred.
 457        /// </exception>
 458        public JsonNumberHandling NumberHandling
 459        {
 0460            get => _numberHandling;
 461            set
 0462            {
 0463                VerifyMutable();
 464
 0465                if (!JsonSerializer.IsValidNumberHandlingValue(value))
 0466                {
 0467                    throw new ArgumentOutOfRangeException(nameof(value));
 468                }
 0469                _numberHandling = value;
 0470            }
 471        }
 472
 473        /// <summary>
 474        /// Specifies preferred object creation handling for properties when deserializing JSON.
 475        /// When set to <see cref="JsonObjectCreationHandling.Populate"/> all properties which
 476        /// are capable of reusing the existing instance will be populated.
 477        /// </summary>
 478        /// <remarks>
 479        /// Only property type is taken into consideration. For example if property is of type
 480        /// <see cref="IEnumerable{T}"/> but it is assigned <see cref="List{T}"/> it will not be populated
 481        /// because <see cref="IEnumerable{T}"/> is not capable of populating.
 482        /// Additionally value types require a setter to be populated.
 483        /// </remarks>
 484        public JsonObjectCreationHandling PreferredObjectCreationHandling
 485        {
 0486            get => _preferredObjectCreationHandling;
 487            set
 0488            {
 0489                VerifyMutable();
 490
 0491                if (!JsonSerializer.IsValidCreationHandlingValue(value))
 0492                {
 0493                    throw new ArgumentOutOfRangeException(nameof(value));
 494                }
 495
 0496                _preferredObjectCreationHandling = value;
 0497            }
 498        }
 499
 500        /// <summary>
 501        /// Determines whether read-only properties are ignored during serialization.
 502        /// A property is read-only if it contains a public getter but not a public setter.
 503        /// The default value is false.
 504        /// </summary>
 505        /// <remarks>
 506        /// Read-only properties are not deserialized regardless of this setting.
 507        /// </remarks>
 508        /// <exception cref="InvalidOperationException">
 509        /// Thrown if this property is set after serialization or deserialization has occurred.
 510        /// </exception>
 511        public bool IgnoreReadOnlyProperties
 512        {
 513            get
 0514            {
 0515                return _ignoreReadOnlyProperties;
 0516            }
 517            set
 0518            {
 0519                VerifyMutable();
 0520                _ignoreReadOnlyProperties = value;
 0521            }
 522        }
 523
 524        /// <summary>
 525        /// Determines whether read-only fields are ignored during serialization.
 526        /// A field is read-only if it is marked with the <c>readonly</c> keyword.
 527        /// The default value is false.
 528        /// </summary>
 529        /// <remarks>
 530        /// Read-only fields are not deserialized regardless of this setting.
 531        /// </remarks>
 532        /// <exception cref="InvalidOperationException">
 533        /// Thrown if this property is set after serialization or deserialization has occurred.
 534        /// </exception>
 535        public bool IgnoreReadOnlyFields
 536        {
 537            get
 0538            {
 0539                return _ignoreReadonlyFields;
 0540            }
 541            set
 0542            {
 0543                VerifyMutable();
 0544                _ignoreReadonlyFields = value;
 0545            }
 546        }
 547
 548        /// <summary>
 549        /// Determines whether fields are handled on serialization and deserialization.
 550        /// The default value is false.
 551        /// </summary>
 552        /// <exception cref="InvalidOperationException">
 553        /// Thrown if this property is set after serialization or deserialization has occurred.
 554        /// </exception>
 555        public bool IncludeFields
 556        {
 557            get
 0558            {
 0559                return _includeFields;
 0560            }
 561            set
 0562            {
 0563                VerifyMutable();
 0564                _includeFields = value;
 0565            }
 566        }
 567
 568        /// <summary>
 569        /// Gets or sets the maximum depth allowed when serializing or deserializing JSON, with the default (i.e. 0) ind
 570        /// </summary>
 571        /// <exception cref="InvalidOperationException">
 572        /// Thrown if this property is set after serialization or deserialization has occurred.
 573        /// </exception>
 574        /// <exception cref="ArgumentOutOfRangeException">
 575        /// Thrown when the max depth is set to a negative value.
 576        /// </exception>
 577        /// <remarks>
 578        /// Going past this depth will throw a <exception cref="JsonException"/>.
 579        /// </remarks>
 580        public int MaxDepth
 581        {
 0582            get => _maxDepth;
 583            set
 0584            {
 0585                VerifyMutable();
 586
 0587                if (value < 0)
 0588                {
 0589                    ThrowHelper.ThrowArgumentOutOfRangeException_MaxDepthMustBePositive(nameof(value));
 590                }
 591
 0592                _maxDepth = value;
 0593                EffectiveMaxDepth = (value == 0 ? DefaultMaxDepth : value);
 0594            }
 595        }
 596
 0597        internal int EffectiveMaxDepth { get; private set; } = DefaultMaxDepth;
 598
 599        /// <summary>
 600        /// Specifies the policy used to convert a property's name on an object to another format, such as camel-casing.
 601        /// The resulting property name is expected to match the JSON payload during deserialization, and
 602        /// will be used when writing the property name during serialization.
 603        /// </summary>
 604        /// <remarks>
 605        /// The policy is not used for properties that have a <see cref="JsonPropertyNameAttribute"/> applied.
 606        /// This property can be set to <see cref="JsonNamingPolicy.CamelCase"/> to specify a camel-casing policy.
 607        /// </remarks>
 608        public JsonNamingPolicy? PropertyNamingPolicy
 609        {
 610            get
 0611            {
 0612                return _jsonPropertyNamingPolicy;
 0613            }
 614            set
 0615            {
 0616                VerifyMutable();
 0617                _jsonPropertyNamingPolicy = value;
 0618            }
 619        }
 620
 621        /// <summary>
 622        /// Determines whether a property's name uses a case-insensitive comparison during deserialization.
 623        /// The default value is false.
 624        /// </summary>
 625        /// <remarks>There is a performance cost associated when the value is true.</remarks>
 626        public bool PropertyNameCaseInsensitive
 627        {
 628            get
 0629            {
 0630                return _propertyNameCaseInsensitive;
 0631            }
 632            set
 0633            {
 0634                VerifyMutable();
 0635                _propertyNameCaseInsensitive = value;
 0636            }
 637        }
 638
 639        /// <summary>
 640        /// Defines how the comments are handled during deserialization.
 641        /// </summary>
 642        /// <exception cref="InvalidOperationException">
 643        /// Thrown if this property is set after serialization or deserialization has occurred.
 644        /// </exception>
 645        /// <exception cref="ArgumentOutOfRangeException">
 646        /// Thrown when the comment handling enum is set to a value that is not supported (or not within the <see cref="
 647        /// </exception>
 648        /// <remarks>
 649        /// By default <exception cref="JsonException"/> is thrown if a comment is encountered.
 650        /// </remarks>
 651        public JsonCommentHandling ReadCommentHandling
 652        {
 653            get
 0654            {
 0655                return _readCommentHandling;
 0656            }
 657            set
 0658            {
 0659                VerifyMutable();
 660
 0661                Debug.Assert(value >= 0);
 0662                if (value > JsonCommentHandling.Skip)
 0663                    throw new ArgumentOutOfRangeException(nameof(value), SR.JsonSerializerDoesNotSupportComments);
 664
 0665                _readCommentHandling = value;
 0666            }
 667        }
 668
 669        /// <summary>
 670        /// Defines how deserializing a type declared as an <see cref="object"/> is handled during deserialization.
 671        /// </summary>
 672        public JsonUnknownTypeHandling UnknownTypeHandling
 673        {
 0674            get => _unknownTypeHandling;
 675            set
 0676            {
 0677                VerifyMutable();
 0678                _unknownTypeHandling = value;
 0679            }
 680        }
 681
 682        /// <summary>
 683        /// Determines how <see cref="JsonSerializer"/> handles JSON properties that
 684        /// cannot be mapped to a specific .NET member when deserializing object types.
 685        /// </summary>
 686        public JsonUnmappedMemberHandling UnmappedMemberHandling
 687        {
 0688            get => _unmappedMemberHandling;
 689            set
 0690            {
 0691                VerifyMutable();
 0692                _unmappedMemberHandling = value;
 0693            }
 694        }
 695
 696        /// <summary>
 697        /// Defines whether JSON should pretty print which includes:
 698        /// indenting nested JSON tokens, adding new lines, and adding white space between property names and values.
 699        /// By default, the JSON is serialized without any extra white space.
 700        /// </summary>
 701        /// <exception cref="InvalidOperationException">
 702        /// Thrown if this property is set after serialization or deserialization has occurred.
 703        /// </exception>
 704        public bool WriteIndented
 705        {
 706            get
 0707            {
 0708                return _writeIndented;
 0709            }
 710            set
 0711            {
 0712                VerifyMutable();
 0713                _writeIndented = value;
 0714            }
 715        }
 716
 717        /// <summary>
 718        /// Defines the indentation character being used when <see cref="WriteIndented" /> is enabled. Defaults to the s
 719        /// </summary>
 720        /// <remarks>Allowed characters are space and horizontal tab.</remarks>
 721        /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> contains an invalid character.</excep
 722        /// <exception cref="InvalidOperationException">
 723        /// Thrown if this property is set after serialization or deserialization has occurred.
 724        /// </exception>
 725        public char IndentCharacter
 726        {
 727            get
 0728            {
 0729                return _indentCharacter;
 0730            }
 731            set
 0732            {
 0733                JsonWriterHelper.ValidateIndentCharacter(value);
 0734                VerifyMutable();
 0735                _indentCharacter = value;
 0736            }
 737        }
 738
 739        /// <summary>
 740        /// Defines the indentation size being used when <see cref="WriteIndented" /> is enabled. Defaults to two.
 741        /// </summary>
 742        /// <remarks>Allowed values are all integers between 0 and 127, included.</remarks>
 743        /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> is out of the allowed range.</excepti
 744        /// <exception cref="InvalidOperationException">
 745        /// Thrown if this property is set after serialization or deserialization has occurred.
 746        /// </exception>
 747        public int IndentSize
 748        {
 749            get
 0750            {
 0751                return _indentSize;
 0752            }
 753            set
 0754            {
 0755                JsonWriterHelper.ValidateIndentSize(value);
 0756                VerifyMutable();
 0757                _indentSize = value;
 0758            }
 759        }
 760
 761        /// <summary>
 762        /// Configures how object references are handled when reading and writing JSON.
 763        /// </summary>
 764        public ReferenceHandler? ReferenceHandler
 765        {
 0766            get => _referenceHandler;
 767            set
 0768            {
 0769                VerifyMutable();
 0770                _referenceHandler = value;
 0771                ReferenceHandlingStrategy = value?.HandlingStrategy ?? JsonKnownReferenceHandler.Unspecified;
 0772            }
 773        }
 774
 775        /// <summary>
 776        /// Gets or sets the new line string to use when <see cref="WriteIndented"/> is <see langword="true"/>.
 777        /// The default is the value of <see cref="Environment.NewLine"/>.
 778        /// </summary>
 779        /// <exception cref="ArgumentNullException">
 780        /// Thrown when the new line string is <see langword="null"/>.
 781        /// </exception>
 782        /// <exception cref="ArgumentOutOfRangeException">
 783        /// Thrown when the new line string is not <c>\n</c> or <c>\r\n</c>.
 784        /// </exception>
 785        /// <exception cref="InvalidOperationException">
 786        /// Thrown if this property is set after serialization or deserialization has occurred.
 787        /// </exception>
 788        public string NewLine
 789        {
 790            get
 0791            {
 0792                return _newLine ??= Environment.NewLine;
 0793            }
 794            set
 0795            {
 0796                JsonWriterHelper.ValidateNewLine(value);
 0797                VerifyMutable();
 0798                _newLine = value;
 0799            }
 800        }
 801
 802        /// <summary>
 803        /// Gets or sets a value that indicates whether nullability annotations should be respected during serialization
 804        /// </summary>
 805        /// <exception cref="InvalidOperationException">
 806        /// Thrown if this property is set after serialization or deserialization has occurred.
 807        /// </exception>
 808        /// <remarks>
 809        /// Nullability annotations are resolved from the properties, fields and constructor parameters
 810        /// that are used by the serializer. This includes annotations stemming from attributes such as
 811        /// <see cref="NotNullAttribute"/>, <see cref="MaybeNullAttribute"/>,
 812        /// <see cref="AllowNullAttribute"/> and <see cref="DisallowNullAttribute"/>.
 813        ///
 814        /// Due to restrictions in how nullable reference types are represented at run time,
 815        /// this setting only governs nullability annotations of non-generic properties and fields.
 816        /// It cannot be used to enforce nullability annotations of root-level types or generic parameters.
 817        ///
 818        /// The default setting for this property can be toggled application-wide using the
 819        /// "System.Text.Json.Serialization.RespectNullableAnnotationsDefault" feature switch.
 820        /// </remarks>
 821        public bool RespectNullableAnnotations
 822        {
 0823            get => _respectNullableAnnotations;
 824            set
 0825            {
 0826                VerifyMutable();
 0827                _respectNullableAnnotations = value;
 0828            }
 829        }
 830
 831        /// <summary>
 832        /// Gets or sets a value that indicates whether non-optional constructor parameters should be specified during d
 833        /// </summary>
 834        /// <exception cref="InvalidOperationException">
 835        /// Thrown if this property is set after serialization or deserialization has occurred.
 836        /// </exception>
 837        /// <remarks>
 838        /// For historical reasons constructor-based deserialization treats all constructor parameters as optional by de
 839        /// This flag allows users to toggle that behavior as necessary for each <see cref="JsonSerializerOptions"/> ins
 840        ///
 841        /// The default setting for this property can be toggled application-wide using the
 842        /// "System.Text.Json.Serialization.RespectRequiredConstructorParametersDefault" feature switch.
 843        /// </remarks>
 844        public bool RespectRequiredConstructorParameters
 845        {
 0846            get => _respectRequiredConstructorParameters;
 847            set
 0848            {
 0849                VerifyMutable();
 0850                _respectRequiredConstructorParameters = value;
 0851            }
 852        }
 853
 854        /// <summary>
 855        /// Defines whether duplicate property names are allowed when deserializing JSON objects.
 856        /// </summary>
 857        /// <exception cref="InvalidOperationException">
 858        /// Thrown if this property is set after serialization or deserialization has occurred.
 859        /// </exception>
 860        /// <remarks>
 861        /// <para>
 862        /// By default, it's set to true. If set to false, <see cref="JsonException"/> is thrown
 863        /// when a duplicate property name is encountered during deserialization.
 864        /// </para>
 865        /// <para>
 866        /// Duplicate property names are not allowed in serialization.
 867        /// </para>
 868        /// </remarks>
 869        public bool AllowDuplicateProperties
 870        {
 0871            get => _allowDuplicateProperties;
 872            set
 0873            {
 0874                VerifyMutable();
 0875                _allowDuplicateProperties = value;
 0876            }
 877        }
 878
 879        /// <summary>
 880        /// Gets or sets a value indicating whether polymorphic serialization metadata is inferred for
 881        /// types that the compiler has marked as closed type hierarchies.
 882        /// </summary>
 883        /// <exception cref="InvalidOperationException">
 884        /// Thrown if this property is set after serialization or deserialization has occurred.
 885        /// </exception>
 886        /// <remarks>
 887        /// By default, it's set to <see langword="false"/>. When set to <see langword="true"/>, types that
 888        /// declare a closed set of derived types (and that do not specify an explicit
 889        /// <see cref="Serialization.JsonDerivedTypeAttribute"/> list) are treated as polymorphic. Closed
 890        /// derived types are expanded recursively, and each terminal derived type is registered using its
 891        /// simple name, equivalent to the result of <c>nameof</c>, as its type discriminator.
 892        /// If a type declares one or more <see cref="Serialization.JsonDerivedTypeAttribute"/> registrations,
 893        /// inference is skipped for that type and only the explicitly registered derived types are used.
 894        /// Polymorphism configuration declared on derived types applies to their respective contracts and does
 895        /// not affect inference for the base type.
 896        /// </remarks>
 897        public bool InferClosedTypePolymorphism
 898        {
 0899            get => _inferClosedTypePolymorphism;
 900            set
 0901            {
 0902                VerifyMutable();
 0903                _inferClosedTypePolymorphism = value;
 0904            }
 905        }
 906
 907        /// <summary>
 908        /// Returns true if options uses compatible built-in resolvers or a combination of compatible built-in resolvers
 909        /// </summary>
 910        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 911        internal bool CanUseFastPathSerializationLogic
 912        {
 913            get
 0914            {
 0915                Debug.Assert(IsReadOnly);
 0916                Debug.Assert(TypeInfoResolver is not null);
 0917                return _canUseFastPathSerializationLogic ??= TypeInfoResolver.IsCompatibleWithOptions(this);
 0918            }
 919        }
 920
 921        private bool? _canUseFastPathSerializationLogic;
 922
 923        // The cached value used to determine if ReferenceHandler should use Preserve or IgnoreCycles semantics or None 
 0924        internal JsonKnownReferenceHandler ReferenceHandlingStrategy = JsonKnownReferenceHandler.Unspecified;
 925
 926        /// <summary>
 927        /// Specifies whether the current instance has been locked for user modification.
 928        /// </summary>
 929        /// <remarks>
 930        /// A <see cref="JsonSerializerOptions"/> instance can be locked either if
 931        /// it has been passed to one of the <see cref="JsonSerializer"/> methods,
 932        /// has been associated with a <see cref="JsonSerializerContext"/> instance,
 933        /// or a user explicitly called the <see cref="MakeReadOnly()"/> methods on the instance.
 934        ///
 935        /// Read-only instances use caching when querying <see cref="JsonConverter"/> and <see cref="JsonTypeInfo"/> met
 936        /// </remarks>
 0937        public bool IsReadOnly => _isReadOnly;
 938        private volatile bool _isReadOnly;
 939
 940        /// <summary>
 941        /// Marks the current instance as read-only preventing any further user modification.
 942        /// </summary>
 943        /// <exception cref="InvalidOperationException">The instance does not specify a <see cref="TypeInfoResolver"/> s
 944        /// <remarks>This method is idempotent.</remarks>
 945        public void MakeReadOnly()
 0946        {
 0947            if (_typeInfoResolver is null)
 0948            {
 0949                ThrowHelper.ThrowInvalidOperationException_JsonSerializerOptionsNoTypeInfoResolverSpecified();
 950            }
 951
 0952            _isReadOnly = true;
 0953        }
 954
 955        /// <summary>
 956        /// Marks the current instance as read-only preventing any further user modification.
 957        /// </summary>
 958        /// <param name="populateMissingResolver">Populates unconfigured <see cref="TypeInfoResolver"/> properties with 
 959        /// <exception cref="InvalidOperationException">
 960        /// The instance does not specify a <see cref="TypeInfoResolver"/> setting. Thrown if <paramref name="populateMi
 961        /// -OR-
 962        /// The <see cref="JsonSerializer.IsReflectionEnabledByDefault"/> feature switch has been turned off.
 963        /// </exception>
 964        /// <remarks>
 965        /// When <paramref name="populateMissingResolver"/> is set to <see langword="true" />, configures the instance f
 966        /// the semantics of the <see cref="JsonSerializer"/> methods accepting <see cref="JsonSerializerOptions"/> para
 967        ///
 968        /// This method is idempotent.
 969        /// </remarks>
 970        [RequiresUnreferencedCode("Populating unconfigured TypeInfoResolver properties with the reflection resolver requ
 971        [RequiresDynamicCode("Populating unconfigured TypeInfoResolver properties with the reflection resolver requires 
 972        public void MakeReadOnly(bool populateMissingResolver)
 0973        {
 0974            if (populateMissingResolver)
 0975            {
 0976                if (!_isConfiguredForJsonSerializer)
 0977                {
 0978                    ConfigureForJsonSerializer();
 0979                }
 0980            }
 981            else
 0982            {
 0983                MakeReadOnly();
 0984            }
 985
 0986            Debug.Assert(IsReadOnly);
 0987        }
 988
 989        /// <summary>
 990        /// Configures the instance for use by the JsonSerializer APIs, applying reflection-based fallback where applica
 991        /// </summary>
 992        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 993        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 994        private void ConfigureForJsonSerializer()
 0995        {
 0996            if (JsonSerializer.IsReflectionEnabledByDefault)
 0997            {
 998                // Even if a resolver has already been specified, we need to root
 999                // the default resolver to gain access to the default converters.
 01000                DefaultJsonTypeInfoResolver defaultResolver = DefaultJsonTypeInfoResolver.DefaultInstance;
 1001
 01002                switch (_typeInfoResolver)
 1003                {
 1004                    case null:
 1005                        // Use the default reflection-based resolver if no resolver has been specified.
 01006                        _typeInfoResolver = defaultResolver;
 01007                        break;
 1008
 01009                    case JsonSerializerContext ctx when AppContextSwitchHelper.IsSourceGenReflectionFallbackEnabled:
 1010                        // .NET 6 compatibility mode: enable fallback to reflection metadata for JsonSerializerContext
 01011                        _effectiveJsonTypeInfoResolver = JsonTypeInfoResolver.Combine(ctx, defaultResolver);
 1012
 01013                        if (_cachingContext is { } cachingContext)
 01014                        {
 1015                            // A cache has already been created by the source generator.
 1016                            // Repeat the same configuration routine for that options instance, if different.
 1017                            // Invalidate any cache entries that have already been stored.
 01018                            if (cachingContext.Options != this && !cachingContext.Options._isConfiguredForJsonSerializer
 01019                            {
 01020                                cachingContext.Options.ConfigureForJsonSerializer();
 01021                            }
 1022                            else
 01023                            {
 01024                                cachingContext.Clear();
 01025                            }
 01026                        }
 01027                        break;
 1028                }
 01029            }
 01030            else if (_typeInfoResolver is null or EmptyJsonTypeInfoResolver)
 01031            {
 01032                ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled();
 1033            }
 1034
 01035            Debug.Assert(_typeInfoResolver is not null);
 1036            // NB preserve write order.
 01037            _isReadOnly = true;
 01038            _isConfiguredForJsonSerializer = true;
 01039        }
 1040
 1041        /// <summary>
 1042        /// This flag is supplementary to <see cref="_isReadOnly"/> and is only used to keep track
 1043        /// of source-gen reflection fallback (assuming the IsSourceGenReflectionFallbackEnabled feature switch is on).
 1044        /// This mode necessitates running the <see cref="ConfigureForJsonSerializer"/> method even
 1045        /// for options instances that have been marked as read-only.
 1046        /// </summary>
 1047        private volatile bool _isConfiguredForJsonSerializer;
 1048
 1049        // Only populated in .NET 6 compatibility mode encoding reflection fallback in source gen
 1050        private IJsonTypeInfoResolver? _effectiveJsonTypeInfoResolver;
 1051
 1052        private JsonTypeInfo? GetTypeInfoNoCaching(Type type)
 01053        {
 01054            IJsonTypeInfoResolver? resolver = _effectiveJsonTypeInfoResolver ?? _typeInfoResolver;
 01055            if (resolver is null)
 01056            {
 01057                return null;
 1058            }
 1059
 01060            JsonTypeInfo? info = resolver.GetTypeInfo(type, this);
 1061
 01062            if (info is not null)
 01063            {
 01064                if (info.Type != type)
 01065                {
 01066                    ThrowHelper.ThrowInvalidOperationException_ResolverTypeNotCompatible(type, info.Type);
 1067                }
 1068
 01069                if (info.Options != this)
 01070                {
 01071                    ThrowHelper.ThrowInvalidOperationException_ResolverTypeInfoOptionsNotCompatible();
 1072                }
 01073            }
 1074            else
 01075            {
 01076                Debug.Assert(_effectiveJsonTypeInfoResolver is null, "an effective resolver always returns metadata");
 1077
 01078                if (type == typeof(object))
 01079                {
 1080                    // If the resolver does not provide a JsonTypeInfo<object> instance, fill
 1081                    // with the serialization-only converter to enable polymorphic serialization.
 01082                    var converter = new SlimObjectConverter(resolver);
 01083                    info = new JsonTypeInfo<object>(converter, this);
 01084                }
 01085            }
 1086
 01087            return info;
 01088        }
 1089
 1090        internal JsonDocumentOptions GetDocumentOptions()
 01091        {
 01092            return new JsonDocumentOptions
 01093            {
 01094                AllowDuplicateProperties = AllowDuplicateProperties,
 01095                AllowTrailingCommas = AllowTrailingCommas,
 01096                CommentHandling = ReadCommentHandling,
 01097                MaxDepth = MaxDepth,
 01098            };
 01099        }
 1100
 1101        internal JsonNodeOptions GetNodeOptions()
 01102        {
 01103            return new JsonNodeOptions
 01104            {
 01105                PropertyNameCaseInsensitive = PropertyNameCaseInsensitive
 01106            };
 01107        }
 1108
 1109        internal JsonReaderOptions GetReaderOptions()
 01110        {
 01111            return new JsonReaderOptions
 01112            {
 01113                AllowTrailingCommas = AllowTrailingCommas,
 01114                CommentHandling = ReadCommentHandling,
 01115                MaxDepth = EffectiveMaxDepth
 01116            };
 01117        }
 1118
 1119        internal JsonWriterOptions GetWriterOptions()
 01120        {
 01121            return new JsonWriterOptions
 01122            {
 01123                Encoder = Encoder,
 01124                Indented = WriteIndented,
 01125                IndentCharacter = IndentCharacter,
 01126                IndentSize = IndentSize,
 01127                MaxDepth = EffectiveMaxDepth,
 01128                NewLine = NewLine,
 01129#if !DEBUG
 01130                SkipValidation = true
 01131#endif
 01132            };
 01133        }
 1134
 1135        // Per JSON Lines spec (https://jsonlines.org/) every value must occupy a single line.
 1136        // Indentation must be suppressed regardless of the user-configured WriteIndented setting,
 1137        // and the JsonWriterOptions.NewLine setting is irrelevant when Indented is false.
 1138        internal JsonWriterOptions GetWriterOptionsForJsonLines()
 01139        {
 01140            return new JsonWriterOptions
 01141            {
 01142                Encoder = Encoder,
 01143                MaxDepth = EffectiveMaxDepth,
 01144#if !DEBUG
 01145                SkipValidation = true
 01146#endif
 01147            };
 01148        }
 1149
 1150        internal void VerifyMutable()
 01151        {
 01152            if (_isReadOnly)
 01153            {
 01154                ThrowHelper.ThrowInvalidOperationException_SerializerOptionsReadOnly(_typeInfoResolver as JsonSerializer
 1155            }
 01156        }
 1157
 1158        private sealed class ConverterList : ConfigurationList<JsonConverter>
 1159        {
 1160            private readonly JsonSerializerOptions _options;
 1161
 1162            public ConverterList(JsonSerializerOptions options, IList<JsonConverter>? source = null)
 01163                : base(source)
 01164            {
 01165                _options = options;
 01166            }
 1167
 01168            public override bool IsReadOnly => _options.IsReadOnly;
 01169            protected override void OnCollectionModifying() => _options.VerifyMutable();
 1170        }
 1171
 1172        private sealed class TypeClassifierList : ConfigurationList<JsonTypeClassifierFactory>
 1173        {
 1174            private readonly JsonSerializerOptions _options;
 1175
 1176            public TypeClassifierList(JsonSerializerOptions options, IList<JsonTypeClassifierFactory>? source = null)
 01177                : base(source)
 01178            {
 01179                _options = options;
 01180            }
 1181
 01182            public override bool IsReadOnly => _options.IsReadOnly;
 01183            protected override void OnCollectionModifying() => _options.VerifyMutable();
 1184        }
 1185
 1186        private sealed class OptionsBoundJsonTypeInfoResolverChain : JsonTypeInfoResolverChain
 1187        {
 1188            private JsonSerializerOptions? _options;
 1189
 01190            public OptionsBoundJsonTypeInfoResolverChain(JsonSerializerOptions options)
 01191            {
 01192                _options = options;
 01193                AddFlattened(options._typeInfoResolver);
 01194            }
 1195
 1196            public void DetachFromOptions()
 01197            {
 01198                _options = null;
 01199            }
 1200
 01201            public override bool IsReadOnly => _options?.IsReadOnly is true;
 1202
 1203            protected override void ValidateAddedValue(IJsonTypeInfoResolver item)
 01204            {
 01205                Debug.Assert(item is not null);
 1206
 01207                if (ReferenceEquals(item, this) || ReferenceEquals(item, _options?._typeInfoResolver))
 01208                {
 1209                    // Cannot add the instances in TypeInfoResolver or TypeInfoResolverChain to the chain itself.
 01210                    ThrowHelper.ThrowInvalidOperationException_InvalidChainedResolver();
 01211                }
 01212            }
 1213
 1214            protected override void OnCollectionModifying()
 01215            {
 01216                _options?.VerifyMutable();
 01217            }
 1218
 1219            protected override void OnCollectionModified()
 01220            {
 1221                // Collection modified by the user: replace the main
 1222                // resolver with the resolver chain as our source of truth.
 01223                _options?._typeInfoResolver = this;
 01224            }
 1225        }
 1226
 1227        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 1228        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 1229        private static JsonSerializerOptions GetOrCreateSingleton(
 1230            ref JsonSerializerOptions? location,
 1231            JsonSerializerDefaults defaults)
 01232        {
 01233            var options = new JsonSerializerOptions(defaults)
 01234            {
 01235                // Because we're marking the default instance as read-only,
 01236                // we need to specify a resolver instance for the case where
 01237                // reflection is disabled by default: use one that returns null for all types.
 01238
 01239                TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault
 01240                    ? DefaultJsonTypeInfoResolver.DefaultInstance
 01241                    : JsonTypeInfoResolver.Empty,
 01242
 01243                _isReadOnly = true,
 01244            };
 1245
 01246            return Interlocked.CompareExchange(ref location, options, null) ?? options;
 01247        }
 1248
 1249        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 01250        private string DebuggerDisplay => $"TypeInfoResolver = {(TypeInfoResolver?.ToString() ?? "<null>")}, IsReadOnly 
 1251    }
 1252}
 1253

Methods/Properties

CacheContext()
rCreate()
GetTypeInfo(System.Type)
TryGetTypeInfo(System.Type,System.Text.Json.Serialization.Metadata.JsonTypeInfo&)
GetTypeInfo()
TryGetTypeInfo(System.Text.Json.Serialization.Metadata.JsonTypeInfo`1<T>&)
GetTypeInfoInternal(System.Type,System.Boolean,System.Nullable`1<System.Boolean>,System.Boolean,System.Boolean)
TryGetTypeInfoCached(System.Type,System.Text.Json.Serialization.Metadata.JsonTypeInfo&)
GetTypeInfoForRootType(System.Type,System.Boolean)
TryGetPolymorphicTypeInfoForRootType(System.Object,System.Text.Json.Serialization.Metadata.JsonTypeInfo&)
ObjectTypeInfo()
ClearCaches()
.ctor(System.Text.Json.JsonSerializerOptions,System.Int32)
Options()
HashCode()
Count()
GetOrAddTypeInfo(System.Type,System.Boolean)
TryGetTypeInfo(System.Type,System.Text.Json.Serialization.Metadata.JsonTypeInfo&)
Clear()
GetOrAddCacheEntry(System.Type)
CreateCacheEntry(System.Type,System.Text.Json.JsonSerializerOptions/CachingContext)
FallBackToNearestAncestor(System.Type,System.Text.Json.JsonSerializerOptions/CachingContext/CacheEntry)
DetermineNearestAncestor(System.Type,System.Text.Json.JsonSerializerOptions/CachingContext/CacheEntry)
.ctor(System.Text.Json.Serialization.Metadata.JsonTypeInfo)
.ctor(System.Runtime.ExceptionServices.ExceptionDispatchInfo)
GetResult()
.cctor()
GetOrCreate(System.Text.Json.JsonSerializerOptions)
TryGetContext(System.Text.Json.JsonSerializerOptions,System.Int32,System.Int32&,System.Text.Json.JsonSerializerOptions/CachingContext&)
Equals(System.Text.Json.JsonSerializerOptions,System.Text.Json.JsonSerializerOptions)
CompareLists(System.Text.Json.Serialization.ConfigurationList`1<TValue>,System.Text.Json.Serialization.ConfigurationList`1<TValue>)
GetHashCode(System.Text.Json.JsonSerializerOptions)
AddListHashCode(System.HashCode&,System.Text.Json.Serialization.ConfigurationList`1<TValue>)
AddHashCode(System.HashCode&,TValue)
Converters()
TypeClassifiers()
GetConverter(System.Type)
GetConverterInternal(System.Type)
GetConverterFromList(System.Type)
GetTypeClassifierFromList(System.Text.Json.Serialization.JsonTypeClassifierContext)
ExpandConverterFactory(System.Text.Json.Serialization.JsonConverter,System.Type)
CheckConverterNullabilityIsSameAsPropertyType(System.Text.Json.Serialization.JsonConverter,System.Type)
Default()
Web()
Strict()
.ctor()
.ctor(System.Text.Json.JsonSerializerOptions)
.ctor(System.Text.Json.JsonSerializerDefaults)
TrackOptionsInstance(System.Text.Json.JsonSerializerOptions)
All()
.cctor()
AddContext()
TypeInfoResolver()
TypeInfoResolver(System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver)
TypeInfoResolverChain()
AllowOutOfOrderMetadataProperties()
AllowOutOfOrderMetadataProperties(System.Boolean)
AllowTrailingCommas()
AllowTrailingCommas(System.Boolean)
DefaultBufferSize()
DefaultBufferSize(System.Int32)
Encoder()
Encoder(System.Text.Encodings.Web.JavaScriptEncoder)
DictionaryKeyPolicy()
DictionaryKeyPolicy(System.Text.Json.JsonNamingPolicy)
IgnoreNullValues()
IgnoreNullValues(System.Boolean)
DefaultIgnoreCondition()
DefaultIgnoreCondition(System.Text.Json.Serialization.JsonIgnoreCondition)
NumberHandling()
NumberHandling(System.Text.Json.Serialization.JsonNumberHandling)
PreferredObjectCreationHandling()
PreferredObjectCreationHandling(System.Text.Json.Serialization.JsonObjectCreationHandling)
IgnoreReadOnlyProperties()
IgnoreReadOnlyProperties(System.Boolean)
IgnoreReadOnlyFields()
IgnoreReadOnlyFields(System.Boolean)
IncludeFields()
IncludeFields(System.Boolean)
MaxDepth()
MaxDepth(System.Int32)
EffectiveMaxDepth()
PropertyNamingPolicy()
PropertyNamingPolicy(System.Text.Json.JsonNamingPolicy)
PropertyNameCaseInsensitive()
PropertyNameCaseInsensitive(System.Boolean)
ReadCommentHandling()
ReadCommentHandling(System.Text.Json.JsonCommentHandling)
UnknownTypeHandling()
UnknownTypeHandling(System.Text.Json.Serialization.JsonUnknownTypeHandling)
UnmappedMemberHandling()
UnmappedMemberHandling(System.Text.Json.Serialization.JsonUnmappedMemberHandling)
WriteIndented()
WriteIndented(System.Boolean)
IndentCharacter()
IndentCharacter(System.Char)
IndentSize()
IndentSize(System.Int32)
ReferenceHandler()
ReferenceHandler(System.Text.Json.Serialization.ReferenceHandler)
NewLine()
NewLine(System.String)
RespectNullableAnnotations()
RespectNullableAnnotations(System.Boolean)
RespectRequiredConstructorParameters()
RespectRequiredConstructorParameters(System.Boolean)
AllowDuplicateProperties()
AllowDuplicateProperties(System.Boolean)
InferClosedTypePolymorphism()
InferClosedTypePolymorphism(System.Boolean)
CanUseFastPathSerializationLogic()
IsReadOnly()
MakeReadOnly()
MakeReadOnly(System.Boolean)
ConfigureForJsonSerializer()
GetTypeInfoNoCaching(System.Type)
GetDocumentOptions()
GetNodeOptions()
GetReaderOptions()
GetWriterOptions()
GetWriterOptionsForJsonLines()
VerifyMutable()
.ctor(System.Text.Json.JsonSerializerOptions,System.Collections.Generic.IList`1<System.Text.Json.Serialization.JsonConverter>)
IsReadOnly()
OnCollectionModifying()
.ctor(System.Text.Json.JsonSerializerOptions,System.Collections.Generic.IList`1<System.Text.Json.Serialization.JsonTypeClassifierFactory>)
IsReadOnly()
OnCollectionModifying()
.ctor(System.Text.Json.JsonSerializerOptions)
DetachFromOptions()
IsReadOnly()
ValidateAddedValue(System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver)
OnCollectionModifying()
OnCollectionModified()
GetOrCreateSingleton(System.Text.Json.JsonSerializerOptions&,System.Text.Json.JsonSerializerDefaults)
DebuggerDisplay()