< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 1032
Coverable lines: 1032
Total lines: 1937
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 490
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: GetDefaultFactoryConverters()100%110%
File 1: GetDefaultSimpleConverters()100%110%
File 1: GetBuiltInConverter(...)0%10100%
File 1: TryGetDefaultSimpleConverter(...)0%220%
File 1: GetCustomConverterForMember(...)0%440%
File 1: GetConverterForType(...)0%10100%
File 1: GetConverterFromAttribute(...)0%28280%
File 2: .ctor()100%110%
File 2: .ctor(...)100%110%
File 2: GetTypeInfo(...)0%440%
File 2: CreateJsonTypeInfo(...)100%110%
File 2: .ctor(...)100%110%
File 2: OnCollectionModifying()0%220%
File 2: System.Text.Json.Serialization.Metadata.IBuiltInJsonTypeInfoResolver.IsCompatibleWithOptions(...)0%440%
File 3: CreateTypeInfoCore(...)0%22220%
File 3: PopulatePolymorphismMetadata(...)0%74740%
File 3: GetInferredTypeDiscriminator(System.Type)0%220%
File 3: HasNonDefaultPolymorphismSettings(System.Text.Json.Serialization.JsonPolymorphicAttribute)0%660%
File 3: ResolveOpenGenericDerivedTypes(...)0%14140%
File 3: IsClosedType(...)0%660%
File 3: GetClosedDerivedTypes(...)0%880%
File 3: GetDeclaredDerivedTypes(...)0%16160%
File 3: TryResolveOpenGenericDerivedType(...)0%32320%
File 3: CollectReferencedParameters(...)0%880%
File 3: PopulateProperties(...)0%16160%
File 3: AddMembersDeclaredBySuperType(...)0%24240%
File 3: AddMember(...)0%220%
File 3: CreatePropertyInfo(...)0%14140%
File 3: GetNumberHandlingForType(...)0%220%
File 3: GetObjectCreationHandlingForType(...)0%220%
File 3: GetUnmappedMemberHandling(...)0%220%
File 3: PropertyIsOverriddenAndIgnored(...)0%880%
File 3: PopulateParameterInfoValues(...)0%12120%
File 3: PopulatePropertyInfo(...)0%660%
File 3: DeterminePropertyPolicies(...)0%660%
File 3: DeterminePropertyName(...)0%12120%
File 3: DeterminePropertyIsRequired(...)0%440%
File 3: DeterminePropertyAccessors(...)0%16160%
File 3: DetermineCreateObjectDelegate(...)0%660%
File 3: DeterminePropertyNullability(...)0%440%
File 3: DetermineParameterNullability(...)0%220%
File 4: PopulateUnionMetadata(...)100%110%
File 4: .ctor()100%110%
File 4: Build(...)0%220%
File 4: PopulateUnionTypeClassifier(...)0%660%
File 4: PopulateUnionCases(...)0%10100%
File 4: PopulateUnionDelegates(...)0%660%
File 4: BuildTopologicallySortedCaseEntries()0%440%
File 4: CreateCaseIndex(...)0%220%
File 4: PopulateUnionDeconstructor(...)0%14140%
File 4: PopulateUnionConstructor(...)0%660%
File 4: ResolveUnionCase(...)0%660%
File 4: IsAssignableCase(...)0%440%
File 4: GetUnionValueProperty()0%12120%
File 4: CreateUnionCaseEntry(...)100%110%
File 4: .ctor(...)100%110%
File 4: SortTypesByInheritanceHierarchy(...)0%220%
File 4: GetInheritanceRelatedTypes(System.Type)0%14140%
File 4: TryGetCaseType(...)0%660%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.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;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Reflection;
 8using System.Text.Json.Reflection;
 9using System.Text.Json.Serialization.Converters;
 10
 11namespace System.Text.Json.Serialization.Metadata
 12{
 13    public partial class DefaultJsonTypeInfoResolver
 14    {
 15        private static Dictionary<Type, JsonConverter>? s_defaultSimpleConverters;
 16        private static JsonConverterFactory[]? s_defaultFactoryConverters;
 17
 18        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 19        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 20        private static JsonConverterFactory[] GetDefaultFactoryConverters()
 021        {
 022            return
 023            [
 024                // Check for disallowed types.
 025                new UnsupportedTypeConverterFactory(),
 026                // Nullable converter should always be next since it forwards to any nullable type.
 027                new NullableConverterFactory(),
 028                new EnumConverterFactory(),
 029                new JsonNodeConverterFactory(),
 030                new FSharpTypeConverterFactory(),
 031                new MemoryConverterFactory(),
 032                // IAsyncEnumerable takes precedence over IEnumerable.
 033                new IAsyncEnumerableConverterFactory(),
 034                // IEnumerable should always be second to last since they can convert any IEnumerable.
 035                new IEnumerableConverterFactory(),
 036                // Union converter should be before Object since it converts [JsonUnion] types.
 037                new JsonUnionConverterFactory(),
 038                // Object should always be last since it converts any type.
 039                new ObjectConverterFactory()
 040            ];
 041        }
 42
 43        private static Dictionary<Type, JsonConverter> GetDefaultSimpleConverters()
 044        {
 45            const int NumberOfSimpleConverters = 35;
 046            var converters = new Dictionary<Type, JsonConverter>(NumberOfSimpleConverters);
 47
 48            // Use a dictionary for simple converters.
 49            // When adding to this, update NumberOfSimpleConverters above.
 50            //
 51            // When adding/removing a built-in converter here, also update
 52            // gen/JsonSourceGenerator.Parser.cs::GetSupportedJsonValueTypes so the union
 53            // ambiguity diagnostic (SYSLIB1227) agrees with JsonTypeInfo.BuildUnionValueTypeMap.
 054            Add(JsonMetadataServices.BooleanConverter);
 055            Add(JsonMetadataServices.ByteConverter);
 056            Add(JsonMetadataServices.ByteArrayConverter);
 057            Add(JsonMetadataServices.CharConverter);
 058            Add(JsonMetadataServices.DateTimeConverter);
 059            Add(JsonMetadataServices.DateTimeOffsetConverter);
 60#if NET
 061            Add(JsonMetadataServices.DateOnlyConverter);
 062            Add(JsonMetadataServices.TimeOnlyConverter);
 063            Add(JsonMetadataServices.HalfConverter);
 64#endif
 065            Add(JsonMetadataServices.DoubleConverter);
 066            Add(JsonMetadataServices.DecimalConverter);
 067            Add(JsonMetadataServices.GuidConverter);
 068            Add(JsonMetadataServices.Int16Converter);
 069            Add(JsonMetadataServices.Int32Converter);
 070            Add(JsonMetadataServices.Int64Converter);
 071            Add(JsonMetadataServices.JsonElementConverter);
 072            Add(JsonMetadataServices.JsonDocumentConverter);
 073            Add(JsonMetadataServices.MemoryByteConverter);
 074            Add(JsonMetadataServices.ReadOnlyMemoryByteConverter);
 075            Add(JsonMetadataServices.ObjectConverter);
 076            Add(JsonMetadataServices.SByteConverter);
 077            Add(JsonMetadataServices.SingleConverter);
 078            Add(JsonMetadataServices.StringConverter);
 079            Add(JsonMetadataServices.TimeSpanConverter);
 080            Add(JsonMetadataServices.UInt16Converter);
 081            Add(JsonMetadataServices.UInt32Converter);
 082            Add(JsonMetadataServices.UInt64Converter);
 83#if NET
 084            Add(JsonMetadataServices.Int128Converter);
 085            Add(JsonMetadataServices.UInt128Converter);
 86#endif
 87#if NET11_0_OR_GREATER
 088            Add(JsonMetadataServices.BFloat16Converter);
 089            Add(JsonMetadataServices.Decimal32Converter);
 090            Add(JsonMetadataServices.Decimal64Converter);
 091            Add(JsonMetadataServices.Decimal128Converter);
 92#endif
 093            Add(JsonMetadataServices.UriConverter);
 094            Add(JsonMetadataServices.VersionConverter);
 95
 096            Debug.Assert(converters.Count <= NumberOfSimpleConverters);
 97
 098            return converters;
 99
 100            void Add(JsonConverter converter) =>
 0101                converters.Add(converter.Type!, converter);
 0102        }
 103
 104        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 105        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 106        private static JsonConverter GetBuiltInConverter(Type typeToConvert)
 0107        {
 0108            s_defaultSimpleConverters ??= GetDefaultSimpleConverters();
 0109            s_defaultFactoryConverters ??= GetDefaultFactoryConverters();
 110
 0111            if (s_defaultSimpleConverters.TryGetValue(typeToConvert, out JsonConverter? converter))
 0112            {
 0113                return converter;
 114            }
 115            else
 0116            {
 0117                foreach (JsonConverterFactory factory in s_defaultFactoryConverters)
 0118                {
 0119                    if (factory.CanConvert(typeToConvert))
 0120                    {
 0121                        converter = factory;
 0122                        break;
 123                    }
 0124                }
 125
 126                // Since the object and IEnumerable converters cover all types, we should have a converter.
 0127                Debug.Assert(converter is not null);
 0128                return converter;
 129            }
 0130        }
 131
 132        internal static bool TryGetDefaultSimpleConverter(Type typeToConvert, [NotNullWhen(true)] out JsonConverter? con
 0133        {
 0134            if (s_defaultSimpleConverters is null)
 0135            {
 0136                converter = null;
 0137                return false;
 138            }
 139
 0140            return s_defaultSimpleConverters.TryGetValue(typeToConvert, out converter);
 0141        }
 142
 143        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 144        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 145        private static JsonConverter? GetCustomConverterForMember(Type typeToConvert, MemberInfo memberInfo, JsonSeriali
 0146        {
 0147            Debug.Assert(memberInfo is FieldInfo or PropertyInfo);
 0148            Debug.Assert(typeToConvert != null);
 149
 0150            JsonConverterAttribute? converterAttribute = memberInfo.GetUniqueCustomAttribute<JsonConverterAttribute>(inh
 0151            return converterAttribute is null ? null : GetConverterFromAttribute(converterAttribute, typeToConvert, memb
 0152        }
 153
 154        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 155        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 156        internal static JsonConverter GetConverterForType(Type typeToConvert, JsonSerializerOptions options, bool resolv
 0157        {
 158            // Priority 1: Attempt to get custom converter from the Converters list.
 0159            JsonConverter? converter = options.GetConverterFromList(typeToConvert);
 160
 161            // Priority 2: Attempt to get converter from [JsonConverter] on the type being converted.
 0162            if (resolveJsonConverterAttribute && converter is null)
 0163            {
 0164                JsonConverterAttribute? converterAttribute = typeToConvert.GetUniqueCustomAttribute<JsonConverterAttribu
 0165                if (converterAttribute is not null)
 0166                {
 0167                    converter = GetConverterFromAttribute(converterAttribute, typeToConvert: typeToConvert, memberInfo: 
 0168                }
 0169            }
 170
 171            // Priority 3: Query the built-in converters.
 0172            converter ??= GetBuiltInConverter(typeToConvert);
 173
 174            // Expand if factory converter & validate.
 0175            converter = options.ExpandConverterFactory(converter, typeToConvert);
 0176            if (!converter.Type!.IsInSubtypeRelationshipWith(typeToConvert))
 0177            {
 0178                ThrowHelper.ThrowInvalidOperationException_SerializationConverterNotCompatible(converter.GetType(), type
 179            }
 180
 0181            JsonSerializerOptions.CheckConverterNullabilityIsSameAsPropertyType(converter, typeToConvert);
 0182            return converter;
 0183        }
 184
 185        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 186        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 187        private static JsonConverter GetConverterFromAttribute(JsonConverterAttribute converterAttribute, Type typeToCon
 0188        {
 189            JsonConverter? converter;
 190
 0191            Type declaringType = memberInfo?.DeclaringType ?? typeToConvert;
 0192            Type? converterType = converterAttribute.ConverterType;
 0193            if (converterType == null)
 0194            {
 195                // Allow the attribute to create the converter.
 0196                converter = converterAttribute.CreateConverter(typeToConvert);
 0197                if (converter is null)
 0198                {
 0199                    ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(declaringT
 200                }
 0201            }
 202            else
 0203            {
 204                // Handle open generic converter types (e.g., OptionConverter<> on Option<T>).
 205                // If the converter type is an open generic and the type to convert is a closed generic
 206                // with matching type arity, construct the closed converter type.
 0207                if (converterType.IsGenericTypeDefinition)
 0208                {
 0209                    if (typeToConvert.IsGenericType &&
 0210                        converterType.GetGenericArguments().Length == typeToConvert.GetGenericArguments().Length)
 0211                    {
 0212                        converterType = converterType.MakeGenericType(typeToConvert.GetGenericArguments());
 0213                    }
 214                    else
 0215                    {
 0216                        ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeOpenGenericNotCompat
 217                    }
 0218                }
 219
 0220                ConstructorInfo? ctor = converterType.GetConstructor(Type.EmptyTypes);
 0221                if (!typeof(JsonConverter).IsAssignableFrom(converterType) || ctor == null || !ctor.IsPublic)
 0222                {
 0223                    ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeInvalid(declaringType, m
 224                }
 225
 0226                converter = (JsonConverter)Activator.CreateInstance(converterType)!;
 0227            }
 228
 0229            Debug.Assert(converter is not null);
 0230            if (!converter.CanConvert(typeToConvert))
 0231            {
 0232                Type? underlyingType = Nullable.GetUnderlyingType(typeToConvert);
 0233                if (underlyingType != null && converter.CanConvert(underlyingType))
 0234                {
 0235                    if (converter is JsonConverterFactory converterFactory)
 0236                    {
 0237                        converter = converterFactory.GetConverterInternal(underlyingType, options);
 0238                    }
 239
 240                    // Allow nullable handling to forward to the underlying type's converter.
 0241                    return NullableConverterFactory.CreateValueConverter(underlyingType, converter);
 242                }
 243
 0244                ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(declaringType,
 245            }
 246
 0247            return converter;
 0248        }
 249    }
 250}
 251

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.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.Threading;
 7
 8namespace System.Text.Json.Serialization.Metadata
 9{
 10    /// <summary>
 11    /// Defines the default, reflection-based JSON contract resolver used by System.Text.Json.
 12    /// </summary>
 13    /// <remarks>
 14    /// The contract resolver used by <see cref="JsonSerializerOptions.Default"/>.
 15    /// </remarks>
 16    public partial class DefaultJsonTypeInfoResolver : IJsonTypeInfoResolver, IBuiltInJsonTypeInfoResolver
 17    {
 18        private bool _mutable;
 19
 20        /// <summary>
 21        /// Creates a mutable <see cref="DefaultJsonTypeInfoResolver"/> instance.
 22        /// </summary>
 23        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 24        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 025        public DefaultJsonTypeInfoResolver() : this(mutable: true)
 026        {
 027        }
 28
 29        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 30        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 031        private DefaultJsonTypeInfoResolver(bool mutable)
 032        {
 033            _mutable = mutable;
 034        }
 35
 36        /// <summary>
 37        /// Resolves a JSON contract for a given <paramref name="type"/> and <paramref name="options"/> configuration.
 38        /// </summary>
 39        /// <param name="type">The type for which to resolve a JSON contract.</param>
 40        /// <param name="options">A <see cref="JsonSerializerOptions"/> instance used to determine contract configuratio
 41        /// <returns>A <see cref="JsonTypeInfo"/> defining a reflection-derived JSON contract for <paramref name="type"/
 42        /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="options"/> is <see langwo
 43        /// <remarks>
 44        /// The base implementation of this method will produce a reflection-derived contract
 45        /// and apply any callbacks from the <see cref="Modifiers"/> list.
 46        /// </remarks>
 47        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
 48            Justification = "The ctor is marked RequiresUnreferencedCode.")]
 49        [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
 50            Justification = "The ctor is marked RequiresDynamicCode.")]
 51        public virtual JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
 052        {
 053            ArgumentNullException.ThrowIfNull(type);
 054            ArgumentNullException.ThrowIfNull(options);
 55
 056            _mutable = false;
 57
 058            JsonTypeInfo.ValidateType(type);
 059            JsonTypeInfo typeInfo = CreateJsonTypeInfo(type, options);
 060            typeInfo.OriginatingResolver = this;
 61
 62            // We've finished configuring the metadata, brand the instance as user-unmodified.
 63            // This should be the last update operation in the resolver to avoid resetting the flag.
 064            typeInfo.IsCustomized = false;
 65
 066            if (_modifiers is not null)
 067            {
 068                foreach (Action<JsonTypeInfo> modifier in _modifiers)
 069                {
 070                    modifier(typeInfo);
 071                }
 072            }
 73
 074            return typeInfo;
 075        }
 76
 77        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 78        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 79        private static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options)
 080        {
 081            JsonConverter converter = GetConverterForType(type, options);
 082            return CreateTypeInfoCore(type, converter, options);
 083        }
 84
 85        /// <summary>
 86        /// Gets a list of user-defined callbacks that can be used to modify the initial contract.
 87        /// </summary>
 88        /// <remarks>
 89        /// The modifier list will be rendered immutable after the first <see cref="GetTypeInfo(Type, JsonSerializerOpti
 90        ///
 91        /// Modifier callbacks are called consecutively in the order in which they are specified in the list.
 92        /// </remarks>
 093        public IList<Action<JsonTypeInfo>> Modifiers => _modifiers ??= new ModifierCollection(this);
 94        private ModifierCollection? _modifiers;
 95
 96        private sealed class ModifierCollection : ConfigurationList<Action<JsonTypeInfo>>
 97        {
 98            private readonly DefaultJsonTypeInfoResolver _resolver;
 99
 0100            public ModifierCollection(DefaultJsonTypeInfoResolver resolver)
 0101            {
 0102                _resolver = resolver;
 0103            }
 104
 0105            public override bool IsReadOnly => !_resolver._mutable;
 106            protected override void OnCollectionModifying()
 0107            {
 0108                if (!_resolver._mutable)
 0109                {
 0110                    ThrowHelper.ThrowInvalidOperationException_DefaultTypeInfoResolverImmutable();
 0111                }
 0112            }
 113        }
 114
 115        bool IBuiltInJsonTypeInfoResolver.IsCompatibleWithOptions(JsonSerializerOptions _)
 116            // Metadata generated by the default resolver is compatible by definition,
 117            // provided that no user extensions have been made on the class.
 0118            => _modifiers is null or { Count: 0 } && GetType() == typeof(DefaultJsonTypeInfoResolver);
 119
 120        internal static DefaultJsonTypeInfoResolver DefaultInstance
 121        {
 122            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 123            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 124            get
 0125            {
 0126                if (s_defaultInstance is DefaultJsonTypeInfoResolver result)
 0127                {
 0128                    return result;
 129                }
 130
 0131                var newInstance = new DefaultJsonTypeInfoResolver(mutable: false);
 0132                return Interlocked.CompareExchange(ref s_defaultInstance, newInstance, comparand: null) ?? newInstance;
 0133            }
 134        }
 135
 136        private static DefaultJsonTypeInfoResolver? s_defaultInstance;
 137    }
 138}
 139

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Reflection;
 8using System.Runtime.CompilerServices;
 9using System.Text.Json.Reflection;
 10
 11namespace System.Text.Json.Serialization.Metadata
 12{
 13    public partial class DefaultJsonTypeInfoResolver
 14    {
 15        internal static MemberAccessor MemberAccessor
 16        {
 17            [RequiresUnreferencedCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 18            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 19            get
 020            {
 021                return global::System.Text.Json.Serialization.Metadata.MemberAccessor.Instance;
 022            }
 23        }
 24
 25        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 26        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 27        private static JsonTypeInfo CreateTypeInfoCore(Type type, JsonConverter converter, JsonSerializerOptions options
 028        {
 029            JsonTypeInfo typeInfo = JsonTypeInfo.CreateJsonTypeInfo(type, converter, options);
 30
 031            if (GetNumberHandlingForType(typeInfo.Type) is { } numberHandling)
 032            {
 033                typeInfo.NumberHandling = numberHandling;
 034            }
 35
 036            if (GetObjectCreationHandlingForType(typeInfo.Type) is { } creationHandling)
 037            {
 038                typeInfo.PreferredPropertyObjectCreationHandling = creationHandling;
 039            }
 40
 041            if (GetUnmappedMemberHandling(typeInfo.Type) is { } unmappedMemberHandling
 042                && typeInfo.Kind is JsonTypeInfoKind.Object)
 043            {
 044                typeInfo.UnmappedMemberHandling = unmappedMemberHandling;
 045            }
 46
 047            PopulatePolymorphismMetadata(typeInfo);
 48
 049            typeInfo.MapInterfaceTypesToCallbacks();
 50
 051            Func<object>? createObject = DetermineCreateObjectDelegate(type, converter);
 052            typeInfo.SetCreateObjectIfCompatible(createObject);
 053            typeInfo.CreateObjectForExtensionDataProperty = createObject;
 54
 055            if (typeInfo is { Kind: JsonTypeInfoKind.Object, IsNullable: false })
 056            {
 057                NullabilityInfoContext nullabilityCtx = new();
 58
 059                if (converter.ConstructorIsParameterized)
 060                {
 61                    // NB parameter metadata must be populated *before* property metadata
 62                    // so that properties can be linked to their associated parameters.
 063                    PopulateParameterInfoValues(typeInfo, nullabilityCtx);
 064                }
 65
 066                PopulateProperties(typeInfo, nullabilityCtx);
 67
 068                typeInfo.ConstructorAttributeProvider = typeInfo.Converter.ConstructorInfo;
 069            }
 70
 071            if (typeInfo.Kind is JsonTypeInfoKind.Union)
 072            {
 073                PopulateUnionMetadata(typeInfo);
 074            }
 75
 76            // Plug in any converter configuration -- should be run last.
 077            converter.ConfigureJsonTypeInfo(typeInfo, options);
 078            converter.ConfigureJsonTypeInfoUsingReflection(typeInfo, options);
 079            return typeInfo;
 080        }
 81
 82        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 83        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 84        internal static void PopulatePolymorphismMetadata(JsonTypeInfo typeInfo)
 085        {
 086            Debug.Assert(!typeInfo.IsReadOnly);
 87
 088            JsonPolymorphismOptions? options = JsonPolymorphismOptions.CreateFromAttributeDeclarations(typeInfo.Type, ou
 89
 90            // 'closed' is a C# language feature that can be used on any target framework, including .NET Framework.
 91            // When targeting a runtime that predates System.Runtime.CompilerServices.IsClosedTypeAttribute, the C#
 92            // compiler polyfills the attribute directly into the consuming assembly. We therefore detect it by full
 93            // name (as we do for other compiler-emitted attributes such as RequiredMemberAttribute) instead of via a
 94            // compile-time type reference, so that inference works on every target framework and regardless of whether
 95            // the attribute is provided by the runtime or polyfilled by the compiler.
 96            //
 97            // Consistent with other type-scoped settings such as JsonNumberHandlingAttribute, a value specified on the
 98            // declaration overrides the global JsonSerializerOptions setting; the global value applies when unset.
 099            bool? inferClosedTypePolymorphismOverride = polymorphicAttribute?.InferClosedTypePolymorphismOrNull;
 100
 0101            bool inferClosedTypePolymorphism = inferClosedTypePolymorphismOverride ?? typeInfo.Options.InferClosedTypePo
 102
 103            // A single attribute walk answers both questions. It is skipped entirely unless inference could
 104            // apply, so ordinary type resolution does not pay for it: an explicit opt-in always needs the
 105            // answer, while a global opt-in only matters when the declaration registers no derived types.
 0106            bool isClosedType = false;
 0107            Type[]? closedDerivedTypes = null;
 108
 0109            if (inferClosedTypePolymorphismOverride is true ||
 0110                (inferClosedTypePolymorphism && options is null or { DerivedTypes.Count: 0 }))
 0111            {
 0112                isClosedType = IsClosedType(typeInfo.Type, out closedDerivedTypes);
 0113            }
 114
 0115            if (inferClosedTypePolymorphismOverride is true && !isClosedType)
 0116            {
 117                // Enabling inference on a type that is not closed can never infer derived types, so it is
 118                // always a mistake -- including when the declaration carries explicit JsonDerivedTypeAttribute
 119                // registrations, which would otherwise mask the error behind a working hierarchy. This matches
 120                // the SYSLIB1243 error reported by the source generator.
 0121                ThrowHelper.ThrowInvalidOperationException_InferClosedTypePolymorphismOnNonClosedType(typeInfo.Type);
 122            }
 123
 0124            if (inferClosedTypePolymorphism &&
 0125                options is null or { DerivedTypes.Count: 0 } &&
 0126                closedDerivedTypes is { Length: > 0 } declaredDerivedTypes)
 0127            {
 0128                options ??= new();
 129
 0130                List<Type> inferredDerivedTypes = new(declaredDerivedTypes.Length);
 0131                GetClosedDerivedTypes(declaredDerivedTypes, inferredDerivedTypes);
 132
 0133                foreach (Type derivedType in inferredDerivedTypes)
 0134                {
 135                    // An inferred derived type must be at least as visible as the base type it is
 136                    // being registered under; otherwise there are call sites that can see the base but
 137                    // not the derived type, and source-gen could not emit a reference to it.
 0138                    if (!derivedType.IsAtLeastAsVisibleAs(typeInfo.Type))
 0139                    {
 0140                        ThrowHelper.ThrowInvalidOperationException_InferredDerivedTypeIsNotAccessible(typeInfo.Type, der
 141                    }
 142
 0143                    options.DerivedTypes.Add(new JsonDerivedType(derivedType, GetInferredTypeDiscriminator(derivedType))
 0144                }
 0145            }
 0146            else if (inferClosedTypePolymorphismOverride is false &&
 0147                options is { DerivedTypes.Count: 0 } &&
 0148                polymorphicAttribute is not null &&
 0149                !HasNonDefaultPolymorphismSettings(polymorphicAttribute))
 0150            {
 151                // The declaration explicitly opted out of inference without specifying any other polymorphism
 152                // metadata, so JsonPolymorphicAttribute is being used to exclude the type from a global opt-in
 153                // rather than to declare a hierarchy. Leave the type non-polymorphic instead of failing
 154                // configuration on an empty registration list.
 155                //
 156                // Any other empty registration list -- including a closed type that declares no derived types
 157                // -- falls through to the pre-existing 'should specify at least one derived type' failure that
 158                // JsonPolymorphicAttribute has always produced when no derived types are registered.
 0159                options = null;
 0160            }
 161
 0162            if (options is not null)
 0163            {
 0164                ResolveOpenGenericDerivedTypes(typeInfo.Type, options.DerivedTypes);
 0165                typeInfo.SetPolymorphismOptions(options);
 0166            }
 167
 0168            if (typeInfo.Kind is not JsonTypeInfoKind.Union)
 0169            {
 0170                if (polymorphicAttribute?.TypeClassifier is Type classifierFactoryType)
 0171                {
 0172                    if (!typeof(JsonTypeClassifierFactory).IsAssignableFrom(classifierFactoryType))
 0173                    {
 0174                        ThrowHelper.ThrowInvalidOperationException_TypeClassifierMustDeriveFromJsonTypeClassifierFactory
 175                    }
 176
 0177                    typeInfo.TypeClassifierFactory = (JsonTypeClassifierFactory)Activator.CreateInstance(classifierFacto
 0178                }
 179
 0180                if (typeInfo.PolymorphismOptions is not null &&
 0181                    (typeInfo.TypeClassifierFactory is not null || typeInfo.Options.TypeClassifiers.Count > 0))
 0182                {
 0183                    typeInfo.TypeClassifierResolutionPending = true;
 0184                }
 0185            }
 186
 187            static string GetInferredTypeDiscriminator(Type type)
 0188            {
 0189                string name = type.Name;
 0190                int genericAritySeparatorIndex = name.IndexOf('`');
 0191                return genericAritySeparatorIndex < 0 ? name : name.Substring(0, genericAritySeparatorIndex);
 0192            }
 193
 194            static bool HasNonDefaultPolymorphismSettings(JsonPolymorphicAttribute attribute) =>
 0195                attribute.IgnoreUnrecognizedTypeDiscriminators ||
 0196                attribute.TypeClassifier is not null ||
 0197                attribute.TypeDiscriminatorPropertyName is not null ||
 0198                attribute.UnknownDerivedTypeHandling != default;
 0199        }
 200
 201        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 202        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 203        private static void ResolveOpenGenericDerivedTypes(Type baseType, IList<JsonDerivedType> derivedTypes)
 0204        {
 0205            Type? baseTypeDefinition = null;
 0206            Type[]? baseTypeArgs = null;
 207
 0208            for (int i = 0; i < derivedTypes.Count; i++)
 0209            {
 0210                JsonDerivedType entry = derivedTypes[i];
 211
 0212                if (entry.DerivedType is null || !entry.DerivedType.IsGenericTypeDefinition)
 0213                {
 214                    // entry.DerivedType is annotated non-nullable, but the public
 215                    // JsonDerivedType constructors do not validate the argument, so a
 216                    // [JsonDerivedType(derivedType: null)] attribute (or an explicit
 217                    // null) yields an entry with DerivedType == null. The downstream
 218                    // validation in PolymorphicTypeResolver will surface a friendly
 219                    // diagnostic; skip silently here so the explicit error wins.
 0220                    continue;
 221                }
 222
 0223                if (!baseType.IsGenericType)
 0224                {
 0225                    ThrowHelper.ThrowInvalidOperationException_OpenGenericDerivedTypeCouldNotBeResolved(
 0226                        baseType, entry.DerivedType, SR.Polymorphism_OpenGeneric_Reason_NotAssignable);
 227                }
 228
 0229                baseTypeDefinition ??= baseType.GetGenericTypeDefinition();
 0230                baseTypeArgs ??= baseType.GetGenericArguments();
 231
 0232                if (!TryResolveOpenGenericDerivedType(
 0233                        entry.DerivedType, baseTypeDefinition, baseTypeArgs,
 0234                        out Type? resolvedType, out string? failureReason))
 0235                {
 0236                    ThrowHelper.ThrowInvalidOperationException_OpenGenericDerivedTypeCouldNotBeResolved(
 0237                        baseType, entry.DerivedType, failureReason!);
 238                }
 239
 0240                derivedTypes[i] = new JsonDerivedType(resolvedType!, entry.TypeDiscriminator);
 0241            }
 0242        }
 243
 244        /// <summary>
 245        /// Determines whether <paramref name="type"/> is declared <see langword="closed"/>, reporting the
 246        /// derived types the compiler recorded for it in <paramref name="derivedTypes"/>.
 247        ///
 248        /// The two results are independent: a closed type that declares no derived types is still a closed
 249        /// type, so <paramref name="derivedTypes"/> is <see langword="null"/> both for a type that is not
 250        /// closed and for a closed type with an empty hierarchy. Inference only consults
 251        /// <paramref name="derivedTypes"/> and is therefore inert on any type that is not closed, but
 252        /// diagnosing an explicit <see cref="JsonPolymorphicAttribute.InferClosedTypePolymorphism"/> opt-in
 253        /// has to tell those two cases apart -- the first is an error, the second a valid declaration.
 254        ///
 255        /// The marker attribute is matched by full name because the C# compiler polyfills
 256        /// <c>System.Runtime.CompilerServices.IsClosedTypeAttribute</c> into assemblies that target runtimes
 257        /// without the type, making the polyfilled copy distinct from any runtime-provided one.
 258        /// </summary>
 259        private static bool IsClosedType(Type type, out Type[]? derivedTypes)
 0260        {
 0261            derivedTypes = null;
 262
 0263            foreach (CustomAttributeData attributeData in type.GetCustomAttributesData())
 0264            {
 0265                Type attributeType = attributeData.AttributeType;
 0266                if (attributeType.Name != "IsClosedTypeAttribute" ||
 0267                    attributeType.FullName != "System.Runtime.CompilerServices.IsClosedTypeAttribute")
 0268                {
 0269                    continue;
 270                }
 271
 0272                derivedTypes = GetDeclaredDerivedTypes(attributeData);
 0273                return true;
 274            }
 275
 0276            return false;
 0277        }
 278
 279        private static void GetClosedDerivedTypes(Type[] declaredDerivedTypes, List<Type> derivedTypes)
 0280        {
 0281            foreach (Type derivedType in declaredDerivedTypes)
 0282            {
 0283                Type derivedTypeDefinition = derivedType.IsGenericType
 0284                    ? derivedType.GetGenericTypeDefinition()
 0285                    : derivedType;
 286
 0287                if (IsClosedType(derivedTypeDefinition, out Type[]? nestedDerivedTypes))
 0288                {
 0289                    if (nestedDerivedTypes is not null)
 0290                    {
 0291                        GetClosedDerivedTypes(nestedDerivedTypes, derivedTypes);
 0292                    }
 0293                }
 294                else
 0295                {
 0296                    derivedTypes.Add(derivedType);
 0297                }
 0298            }
 0299        }
 300
 301        /// <summary>
 302        /// Reads the derived-type list from the compiler-emitted closed-type marker attribute. Returns
 303        /// <see langword="null"/> when the hierarchy is empty or the attribute cannot be interpreted.
 304        /// </summary>
 305        private static Type[]? GetDeclaredDerivedTypes(CustomAttributeData attributeData)
 0306        {
 0307            foreach (CustomAttributeNamedArgument namedArgument in attributeData.NamedArguments)
 0308            {
 0309                if (namedArgument.MemberName != "DerivedTypes")
 0310                {
 0311                    continue;
 312                }
 313
 0314                object? value = namedArgument.TypedValue.Value;
 315
 316                // Mono materializes array-valued named arguments directly, while CoreCLR wraps
 317                // each element in a CustomAttributeTypedArgument.
 0318                if (value is Type[] materializedDerivedTypes)
 0319                {
 0320                    foreach (Type? derivedType in materializedDerivedTypes)
 0321                    {
 0322                        if (derivedType is null)
 0323                        {
 0324                            return null;
 325                        }
 0326                    }
 327
 0328                    return materializedDerivedTypes;
 329                }
 330
 0331                if (value is not IList<CustomAttributeTypedArgument> derivedTypeArguments)
 0332                {
 0333                    return null;
 334                }
 335
 0336                Type[] derivedTypes = new Type[derivedTypeArguments.Count];
 0337                for (int i = 0; i < derivedTypes.Length; i++)
 0338                {
 0339                    if (derivedTypeArguments[i].Value is not Type derivedType)
 0340                    {
 0341                        return null;
 342                    }
 343
 0344                    derivedTypes[i] = derivedType;
 0345                }
 346
 0347                return derivedTypes;
 348            }
 349
 350            // The closed-type marker is present but carries no derived types.
 0351            return null;
 0352        }
 353
 354        /// <summary>
 355        /// Reflection-side resolver: closes <paramref name="openDerivedType"/> against the
 356        /// constructed base type identified by (<paramref name="baseTypeDefinition"/>,
 357        /// <paramref name="baseTypeArgs"/>) via structural unification.
 358        ///
 359        /// IMPORTANT: This implementation MIRRORS the source-gen resolver
 360        /// <c>JsonSourceGenerator.Parser.TryResolveOpenGenericDerivedType</c> in
 361        /// gen/JsonSourceGenerator.Parser.cs. Both implementations -- the structural
 362        /// unbound pre-check, the per-ancestor unification, and the ambiguity
 363        /// detection -- must be kept in lockstep so that reflection and source-gen
 364        /// produce the same closed type for the same registration. Any algorithmic
 365        /// change here must be applied in the source-gen mirror as well.
 366        ///
 367        /// Known intentional asymmetry with the source-gen mirror: source-gen rejects a
 368        /// managed value type (e.g. a struct containing reference fields) supplied for a
 369        /// <c>where T : unmanaged</c> constraint because emitting such a closed type would
 370        /// produce a C# compile error (CS8377). The reflection resolver, by contrast,
 371        /// delegates constraint validation to <see cref="Type.MakeGenericType"/>, which only
 372        /// enforces the underlying value-type part of the constraint at runtime (the
 373        /// <c>unmanaged</c> modreq is not surfaced through standard reflection metadata).
 374        /// As a result, reflection accepts managed structs for <c>unmanaged</c>-constrained
 375        /// derived types where source-gen rejects them. This divergence cannot be bridged
 376        /// without emitting invalid C# code on the source-gen side.
 377        /// </summary>
 378        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 379        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 380        private static bool TryResolveOpenGenericDerivedType(
 381            Type openDerivedType,
 382            Type baseTypeDefinition,
 383            Type[] baseTypeArgs,
 384            out Type? closedDerivedType,
 385            out string? failureReason)
 0386        {
 0387            closedDerivedType = null;
 0388            failureReason = null;
 389
 390            // Find every ancestor of the open derived type whose generic type definition matches
 391            // the base type definition. For classes there is at most one such ancestor, but for
 392            // interfaces a derived type can implement the same interface definition multiple times
 393            // with different type arguments (e.g. Derived<T> : IBase<T>, IBase<List<T>>).
 0394            List<Type> matchingBases = new();
 0395            foreach (Type match in openDerivedType.GetMatchingGenericBaseTypes(baseTypeDefinition))
 0396            {
 0397                matchingBases.Add(match);
 0398            }
 399
 0400            if (matchingBases.Count == 0)
 0401            {
 0402                failureReason = SR.Polymorphism_OpenGeneric_Reason_NotAssignable;
 0403                return false;
 404            }
 405
 406            // The full set of generic parameters we must bind includes the parameters of the
 407            // derived type itself plus any parameters declared by enclosing generic types
 408            // (e.g. Outer<T>.Derived needs T bound from the outer class).
 409            // Type.GetGenericArguments() on an open generic type returns this complete set.
 0410            Type[] requiredParams = openDerivedType.GetGenericArguments();
 411
 412            // Structural unbound pre-check: every required parameter must appear at least once
 413            // somewhere in some matching ancestor's type arguments. If a parameter never appears
 414            // at all, no closed base could ever bind it -- the derived definition is malformed
 415            // regardless of which closed base it is registered against.
 0416            HashSet<Type> referencedParams = new();
 0417            foreach (Type mb in matchingBases)
 0418            {
 0419                foreach (Type arg in mb.GetGenericArguments())
 0420                {
 0421                    CollectReferencedParameters(arg, referencedParams);
 0422                }
 0423            }
 0424            foreach (Type required in requiredParams)
 0425            {
 0426                if (!referencedParams.Contains(required))
 0427                {
 0428                    failureReason = SR.Format(SR.Polymorphism_OpenGeneric_Reason_UnboundParameter, required.Name);
 0429                    return false;
 430                }
 0431            }
 432
 0433            Type[]? successfulArgs = null;
 0434            int successCount = 0;
 435
 0436            foreach (Type matchingBase in matchingBases)
 0437            {
 0438                Type[] matchingBaseArgs = matchingBase.GetGenericArguments();
 0439                Debug.Assert(matchingBaseArgs.Length == baseTypeArgs.Length,
 0440                    "matchingBase and baseTypeArgs share the same generic type definition, so arity must match.");
 441
 0442                var substitution = new Dictionary<Type, Type>(requiredParams.Length);
 0443                bool unified = true;
 0444                for (int i = 0; i < matchingBaseArgs.Length; i++)
 0445                {
 0446                    if (!matchingBaseArgs[i].TryUnifyWith(baseTypeArgs[i], substitution))
 0447                    {
 0448                        unified = false;
 0449                        break;
 450                    }
 0451                }
 452
 0453                if (!unified)
 0454                {
 0455                    continue;
 456                }
 457
 458                // Unification succeeded for every position. Every required parameter of the
 459                // derived type definition must be bound by this ancestor; otherwise the
 460                // resulting closed type would have unbound type arguments (an unspeakable type).
 461                // A sibling ancestor may still bind this parameter, so failure here is not fatal.
 0462                Type[] closedArgs = new Type[requiredParams.Length];
 0463                bool allBound = true;
 0464                for (int i = 0; i < requiredParams.Length; i++)
 0465                {
 0466                    if (!substitution.TryGetValue(requiredParams[i], out Type? boundArg))
 0467                    {
 0468                        allBound = false;
 0469                        break;
 470                    }
 471
 0472                    closedArgs[i] = boundArg;
 0473                }
 474
 0475                if (!allBound)
 0476                {
 0477                    continue;
 478                }
 479
 0480                successCount++;
 0481                if (successCount == 1)
 0482                {
 0483                    successfulArgs = closedArgs;
 0484                }
 485                else
 0486                {
 0487                    failureReason = SR.Polymorphism_OpenGeneric_Reason_AmbiguousMatch;
 0488                    return false;
 489                }
 0490            }
 491
 0492            if (successCount == 0 || successfulArgs is null)
 0493            {
 0494                failureReason = SR.Polymorphism_OpenGeneric_Reason_UnificationFailed;
 0495                return false;
 496            }
 497
 498            try
 0499            {
 0500                closedDerivedType = openDerivedType.MakeGenericType(successfulArgs);
 0501                return true;
 502            }
 0503            catch (Exception ex) when (ex is ArgumentException or TypeLoadException)
 0504            {
 505                // Constraint violation or load failure (e.g. unmanaged constraint, which is
 506                // not observable via the standard reflection constraint metadata). We use a
 507                // structured reason rather than ex.Message so that the outer template — which
 508                // appends its own trailing period — never produces a double period.
 0509                failureReason = SR.Polymorphism_OpenGeneric_Reason_ConstraintViolation;
 0510                return false;
 511            }
 0512        }
 513
 514        private static void CollectReferencedParameters(Type pattern, HashSet<Type> set)
 0515        {
 0516            if (pattern.IsGenericParameter)
 0517            {
 0518                set.Add(pattern);
 0519                return;
 520            }
 521
 0522            if (pattern.HasElementType)
 0523            {
 0524                CollectReferencedParameters(pattern.GetElementType()!, set);
 0525                return;
 526            }
 527
 0528            if (pattern.IsGenericType)
 0529            {
 0530                foreach (Type arg in pattern.GetGenericArguments())
 0531                {
 0532                    CollectReferencedParameters(arg, set);
 0533                }
 0534            }
 0535        }
 536
 537        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 538        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 539        private static void PopulateProperties(JsonTypeInfo typeInfo, NullabilityInfoContext nullabilityCtx)
 0540        {
 0541            Debug.Assert(!typeInfo.IsReadOnly);
 0542            Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object);
 543
 544            // SetsRequiredMembersAttribute means that all required members are assigned by constructor and therefore th
 0545            bool constructorHasSetsRequiredMembersAttribute =
 0546                typeInfo.Converter.ConstructorInfo?.HasSetsRequiredMembersAttribute() ?? false;
 547
 548            // Resolve the type-level JsonNamingPolicyAttribute once for the entire type.
 0549            JsonNamingPolicy? typeNamingPolicy = typeInfo.Type.GetUniqueCustomAttribute<JsonNamingPolicyAttribute>(inher
 550
 551            // Resolve type-level [JsonIgnore] once per type, rather than per-member.
 0552            JsonIgnoreCondition? typeIgnoreCondition = typeInfo.Type.GetUniqueCustomAttribute<JsonIgnoreAttribute>(inher
 0553            if (typeIgnoreCondition == JsonIgnoreCondition.Always)
 0554            {
 0555                ThrowHelper.ThrowInvalidOperationException(SR.DefaultIgnoreConditionInvalid);
 556            }
 557
 0558            JsonTypeInfo.PropertyHierarchyResolutionState state = new(typeInfo.Options);
 559
 560            // Walk the type hierarchy starting from the current type up to the base type(s)
 0561            foreach (Type currentType in typeInfo.Type.GetSortedTypeHierarchy())
 0562            {
 0563                if (currentType == typeof(object) ||
 0564                    currentType == typeof(ValueType))
 0565                {
 566                    // Don't process any members for typeof(object) or System.ValueType
 0567                    break;
 568                }
 569
 0570                AddMembersDeclaredBySuperType(
 0571                    typeInfo,
 0572                    currentType,
 0573                    typeNamingPolicy,
 0574                    nullabilityCtx,
 0575                    typeIgnoreCondition,
 0576                    constructorHasSetsRequiredMembersAttribute,
 0577                    ref state);
 0578            }
 579
 0580            if (state.IsPropertyOrderSpecified)
 0581            {
 0582                typeInfo.PropertyList.SortProperties();
 0583            }
 0584        }
 585
 586        private const BindingFlags AllInstanceMembers =
 587            BindingFlags.Instance |
 588            BindingFlags.Public |
 589            BindingFlags.NonPublic |
 590            BindingFlags.DeclaredOnly;
 591
 592
 593        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 594        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 595        private static void AddMembersDeclaredBySuperType(
 596            JsonTypeInfo typeInfo,
 597            Type currentType,
 598            JsonNamingPolicy? typeNamingPolicy,
 599            NullabilityInfoContext nullabilityCtx,
 600            JsonIgnoreCondition? typeIgnoreCondition,
 601            bool constructorHasSetsRequiredMembersAttribute,
 602            ref JsonTypeInfo.PropertyHierarchyResolutionState state)
 0603        {
 0604            Debug.Assert(!typeInfo.IsReadOnly);
 0605            Debug.Assert(currentType.IsAssignableFrom(typeInfo.Type));
 606
 607            // Compiler adds RequiredMemberAttribute to type if any of the members are marked with 'required' keyword.
 0608            bool shouldCheckMembersForRequiredMemberAttribute =
 0609                !constructorHasSetsRequiredMembersAttribute && currentType.HasRequiredMemberAttribute();
 610
 0611            foreach (PropertyInfo propertyInfo in currentType.GetProperties(AllInstanceMembers))
 0612            {
 613                // Ignore indexers and virtual properties that have overrides that were [JsonIgnore]d.
 0614                if (propertyInfo.GetIndexParameters().Length > 0 ||
 0615                    PropertyIsOverriddenAndIgnored(propertyInfo, state.IgnoredProperties))
 0616                {
 0617                    continue;
 618                }
 619
 0620                bool hasJsonIncludeAttribute = propertyInfo.GetCustomAttribute<JsonIncludeAttribute>(inherit: false) is 
 621
 622                // Only include properties that either have a public getter or a public setter or have the JsonIncludeAt
 0623                if (propertyInfo.GetMethod?.IsPublic == true ||
 0624                    propertyInfo.SetMethod?.IsPublic == true ||
 0625                    hasJsonIncludeAttribute)
 0626                {
 0627                    AddMember(
 0628                        typeInfo,
 0629                        typeToConvert: propertyInfo.PropertyType,
 0630                        memberInfo: propertyInfo,
 0631                        typeNamingPolicy,
 0632                        nullabilityCtx,
 0633                        typeIgnoreCondition,
 0634                        shouldCheckMembersForRequiredMemberAttribute,
 0635                        hasJsonIncludeAttribute,
 0636                        ref state);
 0637                }
 0638            }
 639
 0640            foreach (FieldInfo fieldInfo in currentType.GetFields(AllInstanceMembers))
 0641            {
 0642                bool hasJsonIncludeAttribute = fieldInfo.GetCustomAttribute<JsonIncludeAttribute>(inherit: false) is not
 0643                if (hasJsonIncludeAttribute || (fieldInfo.IsPublic && typeInfo.Options.IncludeFields))
 0644                {
 0645                    AddMember(
 0646                        typeInfo,
 0647                        typeToConvert: fieldInfo.FieldType,
 0648                        memberInfo: fieldInfo,
 0649                        typeNamingPolicy,
 0650                        nullabilityCtx,
 0651                        typeIgnoreCondition,
 0652                        shouldCheckMembersForRequiredMemberAttribute,
 0653                        hasJsonIncludeAttribute,
 0654                        ref state);
 0655                }
 0656            }
 0657        }
 658
 659        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 660        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 661        private static void AddMember(
 662            JsonTypeInfo typeInfo,
 663            Type typeToConvert,
 664            MemberInfo memberInfo,
 665            JsonNamingPolicy? typeNamingPolicy,
 666            NullabilityInfoContext nullabilityCtx,
 667            JsonIgnoreCondition? typeIgnoreCondition,
 668            bool shouldCheckForRequiredKeyword,
 669            bool hasJsonIncludeAttribute,
 670            ref JsonTypeInfo.PropertyHierarchyResolutionState state)
 0671        {
 0672            JsonPropertyInfo? jsonPropertyInfo = CreatePropertyInfo(typeInfo, typeToConvert, memberInfo, typeNamingPolic
 0673            if (jsonPropertyInfo is null)
 0674            {
 675                // ignored invalid property
 0676                return;
 677            }
 678
 0679            Debug.Assert(jsonPropertyInfo.Name is not null);
 0680            typeInfo.PropertyList.AddPropertyWithConflictResolution(jsonPropertyInfo, ref state);
 0681        }
 682
 683        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 684        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 685        private static JsonPropertyInfo? CreatePropertyInfo(
 686            JsonTypeInfo typeInfo,
 687            Type typeToConvert,
 688            MemberInfo memberInfo,
 689            JsonNamingPolicy? typeNamingPolicy,
 690            NullabilityInfoContext nullabilityCtx,
 691            JsonIgnoreCondition? typeIgnoreCondition,
 692            JsonSerializerOptions options,
 693            bool shouldCheckForRequiredKeyword,
 694            bool hasJsonIncludeAttribute)
 0695        {
 0696            JsonIgnoreCondition? ignoreCondition = memberInfo.GetCustomAttribute<JsonIgnoreAttribute>(inherit: false)?.C
 697
 698            // Fall back to the type-level [JsonIgnore] if no member-level attribute is specified.
 0699            if (ignoreCondition is null && typeIgnoreCondition is not null)
 0700            {
 701                // WhenWritingNull is invalid for non-nullable value types; treat as Never in that case
 702                // so that the type-level annotation still overrides the global JSO DefaultIgnoreCondition.
 0703                ignoreCondition = typeIgnoreCondition == JsonIgnoreCondition.WhenWritingNull && !typeToConvert.IsNullabl
 0704                    ? JsonIgnoreCondition.Never
 0705                    : typeIgnoreCondition;
 0706            }
 707
 0708            if (JsonTypeInfo.IsInvalidForSerialization(typeToConvert))
 0709            {
 0710                if (ignoreCondition == JsonIgnoreCondition.Always)
 0711                    return null;
 712
 0713                ThrowHelper.ThrowInvalidOperationException_CannotSerializeInvalidType(typeToConvert, memberInfo.Declarin
 714            }
 715
 716            // Resolve any custom converters on the attribute level.
 717            JsonConverter? customConverter;
 718            try
 0719            {
 0720                customConverter = GetCustomConverterForMember(typeToConvert, memberInfo, options);
 0721            }
 0722            catch (InvalidOperationException) when (ignoreCondition == JsonIgnoreCondition.Always)
 0723            {
 724                // skip property altogether if attribute is invalid and the property is ignored
 0725                return null;
 726            }
 727
 0728            JsonPropertyInfo jsonPropertyInfo = typeInfo.CreatePropertyUsingReflection(typeToConvert, declaringType: mem
 0729            PopulatePropertyInfo(jsonPropertyInfo, memberInfo, customConverter, ignoreCondition, nullabilityCtx, shouldC
 0730            return jsonPropertyInfo;
 0731        }
 732
 733        private static JsonNumberHandling? GetNumberHandlingForType(Type type)
 0734        {
 0735            JsonNumberHandlingAttribute? numberHandlingAttribute = type.GetUniqueCustomAttribute<JsonNumberHandlingAttri
 0736            return numberHandlingAttribute?.Handling;
 0737        }
 738
 739        private static JsonObjectCreationHandling? GetObjectCreationHandlingForType(Type type)
 0740        {
 0741            JsonObjectCreationHandlingAttribute? creationHandlingAttribute = type.GetUniqueCustomAttribute<JsonObjectCre
 0742            return creationHandlingAttribute?.Handling;
 0743        }
 744
 745        private static JsonUnmappedMemberHandling? GetUnmappedMemberHandling(Type type)
 0746        {
 0747            JsonUnmappedMemberHandlingAttribute? numberHandlingAttribute = type.GetUniqueCustomAttribute<JsonUnmappedMem
 0748            return numberHandlingAttribute?.UnmappedMemberHandling;
 0749        }
 750
 751        private static bool PropertyIsOverriddenAndIgnored(PropertyInfo propertyInfo, Dictionary<string, JsonPropertyInf
 0752        {
 0753            return propertyInfo.IsVirtual() &&
 0754                ignoredMembers?.TryGetValue(propertyInfo.Name, out JsonPropertyInfo? ignoredMember) == true &&
 0755                ignoredMember.IsVirtual &&
 0756                propertyInfo.PropertyType == ignoredMember.PropertyType;
 0757        }
 758
 759        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 760        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 761        private static void PopulateParameterInfoValues(JsonTypeInfo typeInfo, NullabilityInfoContext nullabilityCtx)
 0762        {
 0763            Debug.Assert(typeInfo.Converter.ConstructorInfo != null);
 0764            ParameterInfo[] parameters = typeInfo.Converter.ConstructorInfo.GetParameters();
 765
 766            // Count non-out parameters - out parameters don't receive values from JSON.
 0767            int nonOutParameterCount = 0;
 0768            foreach (ParameterInfo param in parameters)
 0769            {
 0770                if (!param.IsOut)
 0771                {
 0772                    nonOutParameterCount++;
 0773                }
 0774            }
 775
 0776            JsonParameterInfoValues[] jsonParameters = new JsonParameterInfoValues[nonOutParameterCount];
 777
 0778            int jsonParamIndex = 0;
 0779            for (int i = 0; i < parameters.Length; i++)
 0780            {
 0781                ParameterInfo reflectionInfo = parameters[i];
 782
 783                // Skip out parameters - they don't receive values from JSON deserialization.
 0784                if (reflectionInfo.IsOut)
 0785                {
 0786                    continue;
 787                }
 788
 789                // Trimmed parameter names are reported as null in CoreCLR or "" in Mono.
 0790                if (string.IsNullOrEmpty(reflectionInfo.Name))
 0791                {
 0792                    Debug.Assert(typeInfo.Converter.ConstructorInfo.DeclaringType != null);
 0793                    ThrowHelper.ThrowNotSupportedException_ConstructorContainsNullParameterNames(typeInfo.Converter.Cons
 794                }
 795
 796                // For byref parameters (in/ref), use the underlying element type.
 0797                Type parameterType = reflectionInfo.ParameterType;
 0798                if (parameterType.IsByRef)
 0799                {
 0800                    parameterType = parameterType.GetElementType()!;
 0801                }
 802
 0803                JsonParameterInfoValues jsonInfo = new()
 0804                {
 0805                    Name = reflectionInfo.Name,
 0806                    ParameterType = parameterType,
 0807                    Position = jsonParamIndex, // Use the position in the args array, not the constructor parameter inde
 0808                    HasDefaultValue = reflectionInfo.HasDefaultValue,
 0809                    DefaultValue = reflectionInfo.GetDefaultValue(),
 0810                    IsNullable = DetermineParameterNullability(reflectionInfo, nullabilityCtx) is not NullabilityState.N
 0811                };
 812
 0813                jsonParameters[jsonParamIndex] = jsonInfo;
 0814                jsonParamIndex++;
 0815            }
 816
 0817            typeInfo.PopulateParameterInfoValues(jsonParameters);
 0818        }
 819
 820        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 821        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 822        private static void PopulatePropertyInfo(
 823            JsonPropertyInfo jsonPropertyInfo,
 824            MemberInfo memberInfo,
 825            JsonConverter? customConverter,
 826            JsonIgnoreCondition? ignoreCondition,
 827            NullabilityInfoContext nullabilityCtx,
 828            bool shouldCheckForRequiredKeyword,
 829            bool hasJsonIncludeAttribute,
 830            JsonNamingPolicy? typeNamingPolicy)
 0831        {
 0832            Debug.Assert(jsonPropertyInfo.AttributeProvider is null);
 833
 0834            switch (jsonPropertyInfo.AttributeProvider = memberInfo)
 835            {
 836                case PropertyInfo propertyInfo:
 0837                    jsonPropertyInfo.MemberName = propertyInfo.Name;
 0838                    jsonPropertyInfo.IsVirtual = propertyInfo.IsVirtual();
 0839                    jsonPropertyInfo.MemberType = MemberTypes.Property;
 0840                    break;
 841                case FieldInfo fieldInfo:
 0842                    jsonPropertyInfo.MemberName = fieldInfo.Name;
 0843                    jsonPropertyInfo.MemberType = MemberTypes.Field;
 0844                    break;
 845                default:
 0846                    Debug.Fail("Only FieldInfo and PropertyInfo members are supported.");
 847                    break;
 848            }
 849
 0850            jsonPropertyInfo.CustomConverter = customConverter;
 0851            DeterminePropertyPolicies(jsonPropertyInfo, memberInfo);
 0852            DeterminePropertyName(jsonPropertyInfo, memberInfo, typeNamingPolicy);
 0853            DeterminePropertyIsRequired(jsonPropertyInfo, memberInfo, shouldCheckForRequiredKeyword);
 0854            DeterminePropertyNullability(jsonPropertyInfo, memberInfo, nullabilityCtx);
 855
 0856            if (ignoreCondition != JsonIgnoreCondition.Always)
 0857            {
 0858                jsonPropertyInfo.DetermineReflectionPropertyAccessors(memberInfo, useNonPublicAccessors: hasJsonIncludeA
 0859            }
 860
 0861            jsonPropertyInfo.IgnoreCondition = ignoreCondition;
 0862            jsonPropertyInfo.IsExtensionData = memberInfo.GetCustomAttribute<JsonExtensionDataAttribute>(inherit: false)
 0863        }
 864
 865        private static void DeterminePropertyPolicies(JsonPropertyInfo propertyInfo, MemberInfo memberInfo)
 0866        {
 0867            JsonPropertyOrderAttribute? orderAttr = memberInfo.GetCustomAttribute<JsonPropertyOrderAttribute>(inherit: f
 0868            propertyInfo.Order = orderAttr?.Order ?? 0;
 869
 0870            JsonNumberHandlingAttribute? numberHandlingAttr = memberInfo.GetCustomAttribute<JsonNumberHandlingAttribute>
 0871            propertyInfo.NumberHandling = numberHandlingAttr?.Handling;
 872
 0873            JsonObjectCreationHandlingAttribute? objectCreationHandlingAttr = memberInfo.GetCustomAttribute<JsonObjectCr
 0874            propertyInfo.ObjectCreationHandling = objectCreationHandlingAttr?.Handling;
 0875        }
 876
 877        private static void DeterminePropertyName(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, JsonNamingPolicy
 0878        {
 0879            JsonPropertyNameAttribute? nameAttribute = memberInfo.GetCustomAttribute<JsonPropertyNameAttribute>(inherit:
 880            string? name;
 0881            if (nameAttribute is not null)
 0882            {
 0883                name = nameAttribute.Name;
 0884            }
 885            else
 0886            {
 0887                JsonNamingPolicy? effectivePolicy = memberInfo.GetCustomAttribute<JsonNamingPolicyAttribute>(inherit: fa
 0888                    ?? typeNamingPolicy
 0889                    ?? propertyInfo.Options.PropertyNamingPolicy;
 890
 0891                name = effectivePolicy is not null
 0892                    ? effectivePolicy.ConvertName(memberInfo.Name)
 0893                    : memberInfo.Name;
 0894            }
 895
 0896            if (name is null)
 0897            {
 0898                ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameNull(propertyInfo);
 899            }
 900
 0901            propertyInfo.Name = name;
 0902        }
 903
 904        private static void DeterminePropertyIsRequired(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, bool shoul
 0905        {
 0906            propertyInfo.IsRequired =
 0907                memberInfo.GetCustomAttribute<JsonRequiredAttribute>(inherit: false) is not null
 0908                || (shouldCheckForRequiredKeyword && memberInfo.HasRequiredMemberAttribute());
 0909        }
 910
 911        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 912        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 913        internal static void DeterminePropertyAccessors<T>(JsonPropertyInfo<T> jsonPropertyInfo, MemberInfo memberInfo, 
 0914        {
 0915            Debug.Assert(memberInfo is FieldInfo or PropertyInfo);
 916
 0917            switch (memberInfo)
 918            {
 919                case PropertyInfo propertyInfo:
 0920                    MethodInfo? getMethod = propertyInfo.GetMethod;
 0921                    if (getMethod != null && (getMethod.IsPublic || useNonPublicAccessors))
 0922                    {
 0923                        jsonPropertyInfo.Get = MemberAccessor.CreatePropertyGetter<T>(propertyInfo);
 0924                    }
 925
 0926                    MethodInfo? setMethod = propertyInfo.SetMethod;
 0927                    if (setMethod != null && (setMethod.IsPublic || useNonPublicAccessors))
 0928                    {
 0929                        jsonPropertyInfo.Set = MemberAccessor.CreatePropertySetter<T>(propertyInfo);
 0930                    }
 931
 0932                    break;
 933
 934                case FieldInfo fieldInfo:
 0935                    Debug.Assert(fieldInfo.IsPublic || useNonPublicAccessors);
 936
 0937                    jsonPropertyInfo.Get = MemberAccessor.CreateFieldGetter<T>(fieldInfo);
 938
 0939                    if (!fieldInfo.IsInitOnly)
 0940                    {
 0941                        jsonPropertyInfo.Set = MemberAccessor.CreateFieldSetter<T>(fieldInfo);
 0942                    }
 943
 0944                    break;
 945
 946                default:
 0947                    Debug.Fail($"Invalid MemberInfo type: {memberInfo.MemberType}");
 948                    break;
 949            }
 0950        }
 951
 952        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 953        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 954        private static Func<object>? DetermineCreateObjectDelegate(Type type, JsonConverter converter)
 0955        {
 0956            ConstructorInfo? defaultCtor = null;
 957
 0958            if (converter.ConstructorInfo != null && !converter.ConstructorIsParameterized)
 0959            {
 960                // A parameterless constructor has been resolved by the converter
 961                // (e.g. it might be a non-public ctor with JsonConstructorAttribute).
 0962                defaultCtor = converter.ConstructorInfo;
 0963            }
 964
 965            // Fall back to resolving any public constructors on the type.
 0966            defaultCtor ??= type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, binder: null, Type.EmptyTyp
 967
 0968            return MemberAccessor.CreateParameterlessConstructor(type, defaultCtor);
 0969        }
 970
 971        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 972        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 973        private static void DeterminePropertyNullability(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, Nullabili
 0974        {
 0975            if (!propertyInfo.PropertyTypeCanBeNull)
 0976            {
 0977                return;
 978            }
 979
 980            NullabilityInfo nullabilityInfo;
 0981            if (propertyInfo.MemberType is MemberTypes.Property)
 0982            {
 0983                nullabilityInfo = nullabilityCtx.Create((PropertyInfo)memberInfo);
 0984            }
 985            else
 0986            {
 0987                Debug.Assert(propertyInfo.MemberType is MemberTypes.Field);
 0988                nullabilityInfo = nullabilityCtx.Create((FieldInfo)memberInfo);
 0989            }
 990
 0991            propertyInfo.IsGetNullable = nullabilityInfo.ReadState is not NullabilityState.NotNull;
 0992            propertyInfo.IsSetNullable = nullabilityInfo.WriteState is not NullabilityState.NotNull;
 0993        }
 994
 995        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 996        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 997        private static NullabilityState DetermineParameterNullability(ParameterInfo parameterInfo, NullabilityInfoContex
 0998        {
 0999            if (!parameterInfo.ParameterType.IsNullableType())
 01000            {
 01001                return NullabilityState.NotNull;
 1002            }
 1003
 01004            NullabilityInfo nullability = nullabilityCtx.Create(parameterInfo);
 01005            return nullability.WriteState;
 01006        }
 1007    }
 1008}
 1009

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Union.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.Reflection;
 9using System.Runtime.CompilerServices;
 10using System.Text.Json.Reflection;
 11using System.Text.Json.Serialization;
 12using System.Text.Json.Serialization.Converters;
 13
 14namespace System.Text.Json.Serialization.Metadata
 15{
 16    public partial class DefaultJsonTypeInfoResolver
 17    {
 18        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 19        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 20        internal static void PopulateUnionMetadata(JsonTypeInfo typeInfo)
 021        {
 022            Debug.Assert(!typeInfo.IsReadOnly);
 023            Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Union);
 24
 025            Type unionType = typeInfo.Type;
 26
 027            Type builderType = typeof(UnionMetadataBuilder<>).MakeGenericType(unionType);
 028            var builder = (UnionMetadataBuilder)Activator.CreateInstance(builderType, nonPublic: true)!;
 029            builder.Build(typeInfo);
 030        }
 31
 32        private abstract class UnionMetadataBuilder
 33        {
 34            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 35            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 36            public abstract void Build(JsonTypeInfo typeInfo);
 37        }
 38
 39        private sealed class UnionMetadataBuilder<TUnion> : UnionMetadataBuilder
 40        {
 041            private readonly List<UnionCaseEntry> _caseEntries = new();
 042            private readonly Dictionary<Type, UnionCaseEntry> _entryByCaseType = new();
 43
 44            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 45            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 46            public override void Build(JsonTypeInfo typeInfo)
 047            {
 048                JsonTypeInfo<TUnion> typeInfoOfT = (JsonTypeInfo<TUnion>)typeInfo;
 49
 050                PopulateUnionCases(typeInfoOfT);
 051                if (_caseEntries.Count == 0)
 052                {
 53                    // No discoverable case constructors, return early and leave the type info with an empty
 54                    // case list and null delegates for potential user-side contract fix-up.
 055                    return;
 56                }
 57
 058                PopulateUnionTypeClassifier(typeInfoOfT); // Must happen after union case population.
 059                PopulateUnionDelegates(typeInfoOfT);
 060            }
 61
 62            private static void PopulateUnionTypeClassifier(JsonTypeInfo<TUnion> typeInfo)
 063            {
 064                Debug.Assert(typeInfo.TypeClassifier is null,
 065                    "PopulateTypeClassifier is only invoked from the built-in resolver, before any contract customizatio
 066                    "TypeClassifier must therefore not be set yet.");
 67
 068                JsonUnionAttribute? attr = typeof(TUnion).GetCustomAttribute<JsonUnionAttribute>();
 069                if (attr?.TypeClassifier is { } attrClassifierType)
 070                {
 071                    if (!typeof(JsonTypeClassifierFactory).IsAssignableFrom(attrClassifierType))
 072                    {
 073                        ThrowHelper.ThrowInvalidOperationException_TypeClassifierMustDeriveFromJsonTypeClassifierFactory
 74                    }
 75
 076                    typeInfo.TypeClassifierFactory = (JsonTypeClassifierFactory)Activator.CreateInstance(attrClassifierT
 077                }
 78
 79                // Resolution is deferred to first read of
 80                // JsonTypeInfo.TypeClassifier — at that point the typeInfo is in the
 81                // per-options cache, so re-entrant lookups for the union type itself find the
 82                // partial typeInfo instead of recursing into a fresh resolution.
 083                typeInfo.TypeClassifierResolutionPending = true;
 084            }
 85
 86            /// <summary>
 87            /// Walks the union type's public single-parameter constructors and populates
 88            /// <see cref="JsonTypeInfo.UnionCases"/> in declaration order. Each ctor overload
 89            /// yields a distinct <see cref="JsonUnionCaseInfo"/>; <c>Foo(T)</c> and
 90            /// <c>Foo(Nullable&lt;T&gt;)</c> coexist as separate cases (typeof(T) vs
 91            /// typeof(T?)) so the metadata reflects the declared union shape and does not
 92            /// silently normalize to a supertype. Token-level ambiguity that follows from
 93            /// multiple cases sharing a JSON value shape is surfaced later at deserialize
 94            /// time via <see cref="JsonTypeInfo.UnionAmbiguousValueTypes"/>.
 95            /// </summary>
 96            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 97            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 98            private void PopulateUnionCases(JsonTypeInfo<TUnion> typeInfo)
 099            {
 0100                Debug.Assert(typeInfo.UnionCases.Count == 0,
 0101                    "PopulateUnionCases is only invoked from the built-in resolver, before any contract customization. "
 0102                    "UnionCases must therefore not be populated yet.");
 103
 0104                NullabilityInfoContext nullabilityCtx = new();
 0105                IList<JsonUnionCaseInfo> unionCases = typeInfo.UnionCases;
 106
 0107                foreach (ConstructorInfo ctor in typeof(TUnion).GetConstructors(BindingFlags.Public | BindingFlags.Insta
 0108                {
 0109                    ParameterInfo[] parameters = ctor.GetParameters();
 0110                    if (parameters.Length != 1 ||
 0111                        !TryGetCaseType(parameters[0], nullabilityCtx, out Type? paramType, out bool acceptsNull) ||
 0112                        paramType.GetCustomAttribute<CompilerGeneratedAttribute>() is not null)
 0113                    {
 0114                        continue;
 115                    }
 116
 0117                    if (_entryByCaseType.ContainsKey(paramType))
 0118                    {
 119                        // The C# compiler rejects two single-parameter ctors with the same
 120                        // parameter type, so this is unreachable from normal source. Defensive
 121                        // skip protects against hand-emitted IL with duplicate signatures.
 0122                        continue;
 123                    }
 124
 0125                    UnionCaseEntry entry = CreateUnionCaseEntry(paramType, ctor, acceptsNull);
 0126                    _entryByCaseType.Add(paramType, entry);
 0127                    _caseEntries.Add(entry);
 0128                    unionCases.Add(entry.CaseInfo);
 0129                }
 0130            }
 131
 132            /// <summary>
 133            /// Builds the convention-based <see cref="JsonTypeInfo.UnionDeconstructor"/> and
 134            /// <see cref="JsonTypeInfo.UnionConstructor"/> delegates.
 135            /// </summary>
 136            /// <remarks>
 137            /// Convention-based discovery gets the union value from a public instance
 138            /// <c>object Value</c> property. If the property is absent, the deconstructor is left
 139            /// null and the user must populate it via contract customization.
 140            /// </remarks>
 141            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 142            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 143            private void PopulateUnionDelegates(JsonTypeInfo<TUnion> typeInfo)
 0144            {
 0145                Debug.Assert(_caseEntries.Count > 0);
 146
 0147                PropertyInfo? valueProperty = GetUnionValueProperty();
 0148                if (valueProperty is null)
 0149                {
 0150                    return;
 151                }
 152
 153                // Topologically sort the (declaration-ordered) UnionCases — most-derived first —
 154                // so the nearest-ancestor walk in the deconstructor and constructor delegates
 155                // hits the most-specific declared case before any of its bases.
 0156                UnionCaseEntry[] orderedCases = BuildTopologicallySortedCaseEntries();
 0157                ConcurrentDictionary<Type, UnionCaseEntry?> caseIndex = CreateCaseIndex(orderedCases);
 158
 159                // Pick the canonical nullable case from _caseEntries (the same order as the
 160                // public UnionCases list) rather than from orderedCases. UnionNullableCaseType
 161                // is later cached by scanning UnionCases in order, and JsonUnionConverter
 162                // looks it up to decide which case represents JSON null. If we scanned
 163                // orderedCases instead, a union with multiple nullable cases in an inheritance
 164                // chain (declaration order [Base, Derived]; topological [Derived, Base])
 165                // would advertise Base via UnionNullableCaseType while the constructor/
 166                // deconstructor delegates silently dispatched through Derived.
 0167                UnionCaseEntry? nullableCase = null;
 0168                foreach (UnionCaseEntry entry in _caseEntries)
 0169                {
 0170                    if (entry.CaseInfo.IsNullable)
 0171                    {
 0172                        nullableCase = entry;
 0173                        break;
 174                    }
 0175                }
 176
 0177                PopulateUnionDeconstructor(typeInfo, orderedCases, caseIndex, valueProperty, nullableCase);
 0178                PopulateUnionConstructor(typeInfo, orderedCases, caseIndex, nullableCase);
 0179            }
 180
 181            private UnionCaseEntry[] BuildTopologicallySortedCaseEntries()
 0182            {
 0183                Type[] caseTypes = new Type[_caseEntries.Count];
 0184                for (int i = 0; i < _caseEntries.Count; i++)
 0185                {
 0186                    caseTypes[i] = _caseEntries[i].CaseType;
 0187                }
 188
 0189                Type[] orderedCaseTypes = SortTypesByInheritanceHierarchy(caseTypes, mostDerivedTypesFirst: true);
 0190                UnionCaseEntry[] orderedCases = new UnionCaseEntry[orderedCaseTypes.Length];
 0191                for (int i = 0; i < orderedCaseTypes.Length; i++)
 0192                {
 0193                    orderedCases[i] = _entryByCaseType[orderedCaseTypes[i]];
 0194                }
 195
 0196                return orderedCases;
 0197            }
 198
 199            private static ConcurrentDictionary<Type, UnionCaseEntry?> CreateCaseIndex(UnionCaseEntry[] orderedCases)
 0200            {
 0201                var caseIndex = new ConcurrentDictionary<Type, UnionCaseEntry?>();
 0202                foreach (UnionCaseEntry entry in orderedCases)
 0203                {
 0204                    caseIndex[entry.CaseType] = entry;
 0205                }
 206
 0207                return caseIndex;
 0208            }
 209
 210            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 211            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 212            private static void PopulateUnionDeconstructor(
 213                JsonTypeInfo<TUnion> typeInfo,
 214                UnionCaseEntry[] orderedCases,
 215                ConcurrentDictionary<Type, UnionCaseEntry?> caseIndex,
 216                PropertyInfo valueProperty,
 217                UnionCaseEntry? nullableCase)
 0218            {
 0219                Debug.Assert(typeInfo.UnionDeconstructor is null);
 220
 0221                Func<TUnion, object?> valueAccessor = MemberAccessor.CreatePropertyGetter<TUnion, object?>(valueProperty
 0222                UnionTryGetValueAccessor<TUnion>? chainedTryGetValue = PopulateTryGetValueMethod();
 223
 0224                typeInfo.UnionDeconstructor = (TUnion union) =>
 0225                {
 0226                    // For reference-type unions union may be null -- treat as the canonical null state.
 0227                    if (!typeof(TUnion).IsValueType && (object?)union is null)
 0228                    {
 0229                        return (null, null);
 0230                    }
 0231
 0232                    // Primary path: when the union declares 'bool TryGetValue(out CaseType)'
 0233                    // overloads, defer to the chained accessor which mirrors the C# compiler's
 0234                    // pattern-matching lowering (overloads are tried most-derived-first; first
 0235                    // true wins). A false return falls through to the default ResolveUnionCase
 0236                    // path below so unions with partial TryGetValue coverage still dispatch the
 0237                    // remaining cases by runtime type.
 0238                    if (chainedTryGetValue is not null && chainedTryGetValue(union, out Type? matchedCaseType, out objec
 0239                    {
 0240                        return (matchedCaseType, matchedValue);
 0241                    }
 0242
 0243                    object? value = valueAccessor(union);
 0244                    if (value is null)
 0245                    {
 0246                        // Always succeed on null. Two scenarios converge here:
 0247                        //   (1) default(struct union) has no case set and Value returns null.
 0248                        //   (2) A nullable case was constructed with null (Foo((string?)null)).
 0249                        // When a nullable case exists, dispatch through it so the converter
 0250                        // calls the case's own converter for null. Otherwise return
 0251                        // (null, null) — JsonUnionConverter writes JSON null in that case.
 0252                        return nullableCase is not null
 0253                            ? (nullableCase.CaseType, null)
 0254                            : (null, null);
 0255                    }
 0256
 0257                    Type runtimeType = value.GetType();
 0258                    UnionCaseEntry? entry = ResolveUnionCase(caseIndex, orderedCases, runtimeType);
 0259                    if (entry is null)
 0260                    {
 0261                        ThrowHelper.ThrowJsonException_UnionRuntimeTypeNotMatchedToCase(typeof(TUnion), runtimeType);
 0262                    }
 0263
 0264                    return (entry.CaseType, value);
 0265                };
 266
 267                // Discovers public instance 'bool TryGetValue(out CaseType)' overloads on TUnion
 268                // matching declared case types and folds them into a single chained accessor
 269                // delegate. C# pattern matching for [Union] types lowers 'v is CaseType' to a
 270                // call to such overloads when present, so the reflection deconstructor must
 271                // honor the same convention to stay consistent with the source generator.
 272                [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 273                [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 274                UnionTryGetValueAccessor<TUnion>? PopulateTryGetValueMethod()
 275                {
 276                    KeyValuePair<Type, MethodInfo>[]? entries = PopulateTryGetValueMethods();
 277                    return entries is null
 278                        ? null
 279                        : MemberAccessor.Instance.CreateUnionTryGetValueAccessor<TUnion>(entries);
 280
 281                    // Filter by the literal "TryGetValue" name in the reflection query: it cuts
 282                    // the candidate set down to the overloads we actually care about and also
 283                    // gives the IL trimmer a static signal that this method name is reflected
 284                    // over, so it can root the matching overloads instead of every public
 285                    // instance method on TUnion. The discovered overloads are then ordered
 286                    // most-derived-first via topological sort so when multiple of them can match
 287                    // the same instance the nearest declared case wins (mirrors the C#
 288                    // compiler's pattern-matching lowering on union types).
 289                    [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 290                    [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 291                    KeyValuePair<Type, MethodInfo>[]? PopulateTryGetValueMethods()
 292                    {
 293                        Dictionary<Type, MethodInfo>? methodsByCaseType = null;
 294                        foreach (MemberInfo member in typeof(TUnion).GetMember("TryGetValue", MemberTypes.Method, Bindin
 295                        {
 296                            MethodInfo method = (MethodInfo)member;
 297                            if (method.ReturnType != typeof(bool) || method.IsGenericMethodDefinition)
 298                            {
 299                                continue;
 300                            }
 301
 302                            ParameterInfo[] parameters = method.GetParameters();
 303                            if (parameters.Length != 1)
 304                            {
 305                                continue;
 306                            }
 307
 308                            ParameterInfo parameter = parameters[0];
 309                            if (!parameter.IsOut || !parameter.ParameterType.IsByRef)
 310                            {
 311                                continue;
 312                            }
 313
 314                            Type caseType = parameter.ParameterType.GetElementType()!;
 315                            if (!caseIndex.ContainsKey(caseType))
 316                            {
 317                                continue;
 318                            }
 319
 320                            // First overload per case type wins; ignore later duplicates.
 321                            (methodsByCaseType ??= new()).TryAdd(caseType, method);
 322                        }
 323
 324                        if (methodsByCaseType is null)
 325                        {
 326                            return null;
 327                        }
 328
 329                        Type[] orderedCaseTypes = SortTypesByInheritanceHierarchy(
 330                            new List<Type>(methodsByCaseType.Keys).ToArray(),
 331                            mostDerivedTypesFirst: true);
 332
 333                        KeyValuePair<Type, MethodInfo>[] orderedEntries = new KeyValuePair<Type, MethodInfo>[orderedCase
 334                        for (int i = 0; i < orderedCaseTypes.Length; i++)
 335                        {
 336                            Type caseType = orderedCaseTypes[i];
 337                            orderedEntries[i] = new KeyValuePair<Type, MethodInfo>(caseType, methodsByCaseType[caseType]
 338                        }
 339
 340                        return orderedEntries;
 341                    }
 342                }
 0343            }
 344
 345            private static void PopulateUnionConstructor(
 346                JsonTypeInfo<TUnion> typeInfo,
 347                UnionCaseEntry[] orderedCases,
 348                ConcurrentDictionary<Type, UnionCaseEntry?> caseIndex,
 349                UnionCaseEntry? nullableCase)
 0350            {
 0351                Debug.Assert(typeInfo.UnionConstructor is null,
 0352                    "PopulateUnionConstructor is only invoked from the built-in resolver, before any contract customizat
 353
 0354                typeInfo.UnionConstructor = (Type caseType, object? value) =>
 0355                {
 0356                    if (value is null)
 0357                    {
 0358                        if (nullableCase is null)
 0359                        {
 0360                            ThrowHelper.ThrowJsonException_UnionDoesNotAcceptNull(typeof(TUnion));
 0361                        }
 0362
 0363                        return nullableCase.Constructor(null);
 0364                    }
 0365
 0366                    UnionCaseEntry? entry = ResolveUnionCase(caseIndex, orderedCases, caseType);
 0367                    if (entry is null)
 0368                    {
 0369                        ThrowHelper.ThrowJsonException_UnionRuntimeTypeNotMatchedToCase(typeof(TUnion), caseType);
 0370                    }
 0371
 0372                    return entry.Constructor(value);
 0373                };
 0374            }
 375
 376            private static UnionCaseEntry? ResolveUnionCase(
 377                ConcurrentDictionary<Type, UnionCaseEntry?> caseIndex,
 378                UnionCaseEntry[] orderedCases,
 379                Type runtimeType)
 0380            {
 0381                if (caseIndex.TryGetValue(runtimeType, out UnionCaseEntry? cached))
 0382                {
 0383                    return cached;
 384                }
 385
 386                // orderedCases is topologically sorted (most-derived first), so the first
 387                // ancestor match is also the nearest one.
 0388                UnionCaseEntry? found = null;
 0389                foreach (UnionCaseEntry entry in orderedCases)
 0390                {
 0391                    if (IsAssignableCase(entry.CaseType, runtimeType))
 0392                    {
 0393                        found = entry;
 0394                        break;
 395                    }
 0396                }
 397
 0398                caseIndex[runtimeType] = found;
 0399                return found;
 0400            }
 401
 402            // Mirrors CLR assignment compatibility, plus the boxing rule that a boxed T can
 403            // satisfy a Nullable<T> case parameter (Reflection.IsAssignableFrom alone does
 404            // not model the box-unbox conversion).
 405            private static bool IsAssignableCase(Type caseType, Type runtimeType)
 0406            {
 0407                if (caseType.IsAssignableFrom(runtimeType))
 0408                {
 0409                    return true;
 410                }
 411
 0412                return Nullable.GetUnderlyingType(caseType) is Type underlying && underlying == runtimeType;
 0413            }
 414
 415            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 416            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 417            private static PropertyInfo? GetUnionValueProperty()
 0418            {
 0419                PropertyInfo? valueProperty = typeof(TUnion).GetProperty("Value", BindingFlags.Public | BindingFlags.Ins
 0420                return valueProperty is { PropertyType: { } propertyType } &&
 0421                    propertyType == typeof(object) &&
 0422                    valueProperty.GetMethod is { IsPublic: true } &&
 0423                    valueProperty.GetIndexParameters().Length == 0
 0424                    ? valueProperty
 0425                    : null;
 0426            }
 427
 428            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 429            [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 430            private static UnionCaseEntry CreateUnionCaseEntry(Type caseType, ConstructorInfo constructorInfo, bool isNu
 0431            {
 0432                Func<object?, TUnion> constructor = MemberAccessor.CreateSingleParameterConstructor<TUnion>(constructorI
 0433                return new UnionCaseEntry(caseType, constructor, isNullable);
 0434            }
 435
 436            private sealed class UnionCaseEntry
 437            {
 0438                public UnionCaseEntry(Type caseType, Func<object?, TUnion> constructor, bool isNullable)
 0439                {
 0440                    CaseType = caseType;
 0441                    Constructor = constructor;
 0442                    CaseInfo = new JsonUnionCaseInfo(caseType) { IsNullable = isNullable };
 0443                }
 444
 0445                public Type CaseType { get; }
 0446                public JsonUnionCaseInfo CaseInfo { get; }
 0447                public Func<object?, TUnion> Constructor { get; }
 448            }
 449        }
 450
 451        private static Type[] SortTypesByInheritanceHierarchy(Type[] types, bool mostDerivedTypesFirst)
 0452        {
 0453            if (types.Length <= 1)
 0454            {
 0455                return types;
 456            }
 457
 0458            Type root = typeof(void);
 0459            Debug.Assert(Array.IndexOf(types, root) < 0);
 460
 461            // Use typeof(void) as a synthetic root: it cannot be a case type and is not
 462            // in a subtype relationship with any valid case type.
 0463            Type[] sortedTypesWithRoot = JsonHelpers.TraverseGraphWithTopologicalSort(root, GetInheritanceRelatedTypes);
 0464            Debug.Assert(sortedTypesWithRoot.Length == types.Length + 1);
 0465            Debug.Assert(sortedTypesWithRoot[0] == root);
 466
 0467            Type[] sortedTypes = new Type[types.Length];
 0468            Array.Copy(sortedTypesWithRoot, sourceIndex: 1, sortedTypes, destinationIndex: 0, sortedTypes.Length);
 469
 0470            return sortedTypes;
 471
 472            ICollection<Type> GetInheritanceRelatedTypes(Type type)
 0473            {
 0474                if (type == root)
 0475                {
 0476                    Type[] rootChildren = new Type[types.Length];
 0477                    for (int i = 0; i < rootChildren.Length; i++)
 0478                    {
 479                        // TraverseGraphWithTopologicalSort writes childless nodes from the
 480                        // end of the result, so enumerate root children in reverse to
 481                        // preserve the input order for unrelated types.
 0482                        rootChildren[i] = types[rootChildren.Length - i - 1];
 0483                    }
 484
 0485                    return rootChildren;
 486                }
 487
 0488                List<Type>? relatedTypes = null;
 0489                foreach (Type candidate in types)
 0490                {
 0491                    bool isRelatedType = mostDerivedTypesFirst
 0492                        ? candidate.IsAssignableFrom(type)
 0493                        : type.IsAssignableFrom(candidate);
 494
 0495                    if (candidate != type && isRelatedType)
 0496                    {
 0497                        (relatedTypes ??= new()).Add(candidate);
 0498                    }
 0499                }
 500
 0501                return relatedTypes ?? (ICollection<Type>)Array.Empty<Type>();
 0502            }
 0503        }
 504
 505        private static bool TryGetCaseType(
 506            ParameterInfo parameter,
 507            NullabilityInfoContext nullabilityCtx,
 508            [NotNullWhen(true)] out Type? caseType,
 509            out bool acceptsNull)
 0510        {
 0511            acceptsNull = false;
 0512            Type parameterType = parameter.ParameterType;
 0513            if (parameterType.IsByRef)
 0514            {
 0515                caseType = null;
 0516                return false;
 517            }
 518
 0519            caseType = parameterType;
 0520            if (Nullable.GetUnderlyingType(parameterType) is not null)
 0521            {
 522                // Value-type Nullable<T> parameter: the case type is the Nullable<T> itself
 523                // (no normalization to the underlying T). The case accepts the null payload.
 524                // A peer Foo(T) ctor declares a distinct, non-null-accepting case.
 0525                acceptsNull = true;
 0526            }
 0527            else if (parameterType.IsNullableType())
 0528            {
 529                // Reference type — read the parameter's nullable annotation.
 0530                NullabilityInfo nullability = nullabilityCtx.Create(parameter);
 0531                acceptsNull = nullability.WriteState is not NullabilityState.NotNull;
 0532            }
 533
 0534            return true;
 0535        }
 536    }
 537}
 538

Methods/Properties

GetDefaultFactoryConverters()
GetDefaultSimpleConverters()
GetBuiltInConverter(System.Type)
TryGetDefaultSimpleConverter(System.Type,System.Text.Json.Serialization.JsonConverter&)
GetCustomConverterForMember(System.Type,System.Reflection.MemberInfo,System.Text.Json.JsonSerializerOptions)
GetConverterForType(System.Type,System.Text.Json.JsonSerializerOptions,System.Boolean)
GetConverterFromAttribute(System.Text.Json.Serialization.JsonConverterAttribute,System.Type,System.Reflection.MemberInfo,System.Text.Json.JsonSerializerOptions)
.ctor()
.ctor(System.Boolean)
GetTypeInfo(System.Type,System.Text.Json.JsonSerializerOptions)
CreateJsonTypeInfo(System.Type,System.Text.Json.JsonSerializerOptions)
Modifiers()
.ctor(System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver)
IsReadOnly()
OnCollectionModifying()
System.Text.Json.Serialization.Metadata.IBuiltInJsonTypeInfoResolver.IsCompatibleWithOptions(System.Text.Json.JsonSerializerOptions)
DefaultInstance()
MemberAccessor()
CreateTypeInfoCore(System.Type,System.Text.Json.Serialization.JsonConverter,System.Text.Json.JsonSerializerOptions)
PopulatePolymorphismMetadata(System.Text.Json.Serialization.Metadata.JsonTypeInfo)
GetInferredTypeDiscriminator(System.Type)
HasNonDefaultPolymorphismSettings(System.Text.Json.Serialization.JsonPolymorphicAttribute)
ResolveOpenGenericDerivedTypes(System.Type,System.Collections.Generic.IList`1<System.Text.Json.Serialization.Metadata.JsonDerivedType>)
IsClosedType(System.Type,System.Type[]&)
GetClosedDerivedTypes(System.Type[],System.Collections.Generic.List`1<System.Type>)
GetDeclaredDerivedTypes(System.Reflection.CustomAttributeData)
TryResolveOpenGenericDerivedType(System.Type,System.Type,System.Type[],System.Type&,System.String&)
CollectReferencedParameters(System.Type,System.Collections.Generic.HashSet`1<System.Type>)
PopulateProperties(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Reflection.NullabilityInfoContext)
AddMembersDeclaredBySuperType(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Type,System.Text.Json.JsonNamingPolicy,System.Reflection.NullabilityInfoContext,System.Nullable`1<System.Text.Json.Serialization.JsonIgnoreCondition>,System.Boolean,System.Text.Json.Serialization.Metadata.JsonTypeInfo/PropertyHierarchyResolutionState&)
AddMember(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Type,System.Reflection.MemberInfo,System.Text.Json.JsonNamingPolicy,System.Reflection.NullabilityInfoContext,System.Nullable`1<System.Text.Json.Serialization.JsonIgnoreCondition>,System.Boolean,System.Boolean,System.Text.Json.Serialization.Metadata.JsonTypeInfo/PropertyHierarchyResolutionState&)
CreatePropertyInfo(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Type,System.Reflection.MemberInfo,System.Text.Json.JsonNamingPolicy,System.Reflection.NullabilityInfoContext,System.Nullable`1<System.Text.Json.Serialization.JsonIgnoreCondition>,System.Text.Json.JsonSerializerOptions,System.Boolean,System.Boolean)
GetNumberHandlingForType(System.Type)
GetObjectCreationHandlingForType(System.Type)
GetUnmappedMemberHandling(System.Type)
PropertyIsOverriddenAndIgnored(System.Reflection.PropertyInfo,System.Collections.Generic.Dictionary`2<System.String,System.Text.Json.Serialization.Metadata.JsonPropertyInfo>)
PopulateParameterInfoValues(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Reflection.NullabilityInfoContext)
PopulatePropertyInfo(System.Text.Json.Serialization.Metadata.JsonPropertyInfo,System.Reflection.MemberInfo,System.Text.Json.Serialization.JsonConverter,System.Nullable`1<System.Text.Json.Serialization.JsonIgnoreCondition>,System.Reflection.NullabilityInfoContext,System.Boolean,System.Boolean,System.Text.Json.JsonNamingPolicy)
DeterminePropertyPolicies(System.Text.Json.Serialization.Metadata.JsonPropertyInfo,System.Reflection.MemberInfo)
DeterminePropertyName(System.Text.Json.Serialization.Metadata.JsonPropertyInfo,System.Reflection.MemberInfo,System.Text.Json.JsonNamingPolicy)
DeterminePropertyIsRequired(System.Text.Json.Serialization.Metadata.JsonPropertyInfo,System.Reflection.MemberInfo,System.Boolean)
DeterminePropertyAccessors(System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1<T>,System.Reflection.MemberInfo,System.Boolean)
DetermineCreateObjectDelegate(System.Type,System.Text.Json.Serialization.JsonConverter)
DeterminePropertyNullability(System.Text.Json.Serialization.Metadata.JsonPropertyInfo,System.Reflection.MemberInfo,System.Reflection.NullabilityInfoContext)
DetermineParameterNullability(System.Reflection.ParameterInfo,System.Reflection.NullabilityInfoContext)
PopulateUnionMetadata(System.Text.Json.Serialization.Metadata.JsonTypeInfo)
.ctor()
Build(System.Text.Json.Serialization.Metadata.JsonTypeInfo)
PopulateUnionTypeClassifier(System.Text.Json.Serialization.Metadata.JsonTypeInfo`1<TUnion>)
PopulateUnionCases(System.Text.Json.Serialization.Metadata.JsonTypeInfo`1<TUnion>)
PopulateUnionDelegates(System.Text.Json.Serialization.Metadata.JsonTypeInfo`1<TUnion>)
BuildTopologicallySortedCaseEntries()
CreateCaseIndex(System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver/UnionMetadataBuilder`1/UnionCaseEntry<TUnion>[])
PopulateUnionDeconstructor(System.Text.Json.Serialization.Metadata.JsonTypeInfo`1<TUnion>,System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver/UnionMetadataBuilder`1/UnionCaseEntry<TUnion>[],System.Collections.Concurrent.ConcurrentDictionary`2<System.Type,System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver/UnionMetadataBuilder`1/UnionCaseEntry<TUnion>>,System.Reflection.PropertyInfo,System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver/UnionMetadataBuilder`1/UnionCaseEntry<TUnion>)
PopulateUnionConstructor(System.Text.Json.Serialization.Metadata.JsonTypeInfo`1<TUnion>,System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver/UnionMetadataBuilder`1/UnionCaseEntry<TUnion>[],System.Collections.Concurrent.ConcurrentDictionary`2<System.Type,System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver/UnionMetadataBuilder`1/UnionCaseEntry<TUnion>>,System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver/UnionMetadataBuilder`1/UnionCaseEntry<TUnion>)
ResolveUnionCase(System.Collections.Concurrent.ConcurrentDictionary`2<System.Type,System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver/UnionMetadataBuilder`1/UnionCaseEntry<TUnion>>,System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver/UnionMetadataBuilder`1/UnionCaseEntry<TUnion>[],System.Type)
IsAssignableCase(System.Type,System.Type)
GetUnionValueProperty()
CreateUnionCaseEntry(System.Type,System.Reflection.ConstructorInfo,System.Boolean)
.ctor(System.Type,System.Func`2<System.Object,TUnion>,System.Boolean)
CaseType()
CaseInfo()
Constructor()
SortTypesByInheritanceHierarchy(System.Type[],System.Boolean)
GetInheritanceRelatedTypes(System.Type)
TryGetCaseType(System.Reflection.ParameterInfo,System.Reflection.NullabilityInfoContext,System.Type&,System.Boolean&)