< Summary

Line coverage
39%
Covered lines: 411
Uncovered lines: 621
Coverable lines: 1032
Total lines: 1937
Line coverage: 39.8%
Branch coverage
31%
Covered branches: 152
Total branches: 490
Branch coverage: 31%
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%11100%
File 1: GetDefaultSimpleConverters()100%11100%
File 1: GetBuiltInConverter(...)100%1010100%
File 1: TryGetDefaultSimpleConverter(...)0%220%
File 1: GetCustomConverterForMember(...)50%44100%
File 1: GetConverterForType(...)70%101088.88%
File 1: GetConverterFromAttribute(...)28.57%282834.14%
File 2: .ctor()100%110%
File 2: .ctor(...)100%11100%
File 2: GetTypeInfo(...)25%4464.7%
File 2: CreateJsonTypeInfo(...)100%11100%
File 2: .ctor(...)100%110%
File 2: OnCollectionModifying()0%220%
File 2: System.Text.Json.Serialization.Metadata.IBuiltInJsonTypeInfoResolver.IsCompatibleWithOptions(...)25%44100%
File 3: CreateTypeInfoCore(...)68.18%222268.42%
File 3: PopulatePolymorphismMetadata(...)33.78%747443.1%
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(...)68.75%161683.87%
File 3: AddMembersDeclaredBySuperType(...)66.66%242469.56%
File 3: AddMember(...)50%2275%
File 3: CreatePropertyInfo(...)28.57%141447.82%
File 3: GetNumberHandlingForType(...)50%22100%
File 3: GetObjectCreationHandlingForType(...)50%22100%
File 3: GetUnmappedMemberHandling(...)50%22100%
File 3: PropertyIsOverriddenAndIgnored(...)62.5%88100%
File 3: PopulateParameterInfoValues(...)66.66%121280.95%
File 3: PopulatePropertyInfo(...)33.33%6682.6%
File 3: DeterminePropertyPolicies(...)50%66100%
File 3: DeterminePropertyName(...)50%121273.68%
File 3: DeterminePropertyIsRequired(...)50%44100%
File 3: DeterminePropertyAccessors(...)50%161665.21%
File 3: DetermineCreateObjectDelegate(...)100%66100%
File 3: DeterminePropertyNullability(...)75%4473.33%
File 3: DetermineParameterNullability(...)100%22100%
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()
 121        {
 122            return
 123            [
 124                // Check for disallowed types.
 125                new UnsupportedTypeConverterFactory(),
 126                // Nullable converter should always be next since it forwards to any nullable type.
 127                new NullableConverterFactory(),
 128                new EnumConverterFactory(),
 129                new JsonNodeConverterFactory(),
 130                new FSharpTypeConverterFactory(),
 131                new MemoryConverterFactory(),
 132                // IAsyncEnumerable takes precedence over IEnumerable.
 133                new IAsyncEnumerableConverterFactory(),
 134                // IEnumerable should always be second to last since they can convert any IEnumerable.
 135                new IEnumerableConverterFactory(),
 136                // Union converter should be before Object since it converts [JsonUnion] types.
 137                new JsonUnionConverterFactory(),
 138                // Object should always be last since it converts any type.
 139                new ObjectConverterFactory()
 140            ];
 141        }
 42
 43        private static Dictionary<Type, JsonConverter> GetDefaultSimpleConverters()
 144        {
 45            const int NumberOfSimpleConverters = 35;
 146            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.
 154            Add(JsonMetadataServices.BooleanConverter);
 155            Add(JsonMetadataServices.ByteConverter);
 156            Add(JsonMetadataServices.ByteArrayConverter);
 157            Add(JsonMetadataServices.CharConverter);
 158            Add(JsonMetadataServices.DateTimeConverter);
 159            Add(JsonMetadataServices.DateTimeOffsetConverter);
 60#if NET
 161            Add(JsonMetadataServices.DateOnlyConverter);
 162            Add(JsonMetadataServices.TimeOnlyConverter);
 163            Add(JsonMetadataServices.HalfConverter);
 64#endif
 165            Add(JsonMetadataServices.DoubleConverter);
 166            Add(JsonMetadataServices.DecimalConverter);
 167            Add(JsonMetadataServices.GuidConverter);
 168            Add(JsonMetadataServices.Int16Converter);
 169            Add(JsonMetadataServices.Int32Converter);
 170            Add(JsonMetadataServices.Int64Converter);
 171            Add(JsonMetadataServices.JsonElementConverter);
 172            Add(JsonMetadataServices.JsonDocumentConverter);
 173            Add(JsonMetadataServices.MemoryByteConverter);
 174            Add(JsonMetadataServices.ReadOnlyMemoryByteConverter);
 175            Add(JsonMetadataServices.ObjectConverter);
 176            Add(JsonMetadataServices.SByteConverter);
 177            Add(JsonMetadataServices.SingleConverter);
 178            Add(JsonMetadataServices.StringConverter);
 179            Add(JsonMetadataServices.TimeSpanConverter);
 180            Add(JsonMetadataServices.UInt16Converter);
 181            Add(JsonMetadataServices.UInt32Converter);
 182            Add(JsonMetadataServices.UInt64Converter);
 83#if NET
 184            Add(JsonMetadataServices.Int128Converter);
 185            Add(JsonMetadataServices.UInt128Converter);
 86#endif
 87#if NET11_0_OR_GREATER
 188            Add(JsonMetadataServices.BFloat16Converter);
 189            Add(JsonMetadataServices.Decimal32Converter);
 190            Add(JsonMetadataServices.Decimal64Converter);
 191            Add(JsonMetadataServices.Decimal128Converter);
 92#endif
 193            Add(JsonMetadataServices.UriConverter);
 194            Add(JsonMetadataServices.VersionConverter);
 95
 196            Debug.Assert(converters.Count <= NumberOfSimpleConverters);
 97
 198            return converters;
 99
 100            void Add(JsonConverter converter) =>
 35101                converters.Add(converter.Type!, converter);
 1102        }
 103
 104        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 105        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 106        private static JsonConverter GetBuiltInConverter(Type typeToConvert)
 4061107        {
 4061108            s_defaultSimpleConverters ??= GetDefaultSimpleConverters();
 4061109            s_defaultFactoryConverters ??= GetDefaultFactoryConverters();
 110
 4061111            if (s_defaultSimpleConverters.TryGetValue(typeToConvert, out JsonConverter? converter))
 2666112            {
 2666113                return converter;
 114            }
 115            else
 1395116            {
 23482117                foreach (JsonConverterFactory factory in s_defaultFactoryConverters)
 10346118                {
 10346119                    if (factory.CanConvert(typeToConvert))
 1395120                    {
 1395121                        converter = factory;
 1395122                        break;
 123                    }
 8951124                }
 125
 126                // Since the object and IEnumerable converters cover all types, we should have a converter.
 1395127                Debug.Assert(converter is not null);
 1395128                return converter;
 129            }
 4061130        }
 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
 2976146        {
 2976147            Debug.Assert(memberInfo is FieldInfo or PropertyInfo);
 2976148            Debug.Assert(typeToConvert != null);
 149
 2976150            JsonConverterAttribute? converterAttribute = memberInfo.GetUniqueCustomAttribute<JsonConverterAttribute>(inh
 2976151            return converterAttribute is null ? null : GetConverterFromAttribute(converterAttribute, typeToConvert, memb
 2976152        }
 153
 154        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 155        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 156        internal static JsonConverter GetConverterForType(Type typeToConvert, JsonSerializerOptions options, bool resolv
 4147157        {
 158            // Priority 1: Attempt to get custom converter from the Converters list.
 4147159            JsonConverter? converter = options.GetConverterFromList(typeToConvert);
 160
 161            // Priority 2: Attempt to get converter from [JsonConverter] on the type being converted.
 4147162            if (resolveJsonConverterAttribute && converter is null)
 4147163            {
 4147164                JsonConverterAttribute? converterAttribute = typeToConvert.GetUniqueCustomAttribute<JsonConverterAttribu
 4147165                if (converterAttribute is not null)
 86166                {
 86167                    converter = GetConverterFromAttribute(converterAttribute, typeToConvert: typeToConvert, memberInfo: 
 86168                }
 4147169            }
 170
 171            // Priority 3: Query the built-in converters.
 4147172            converter ??= GetBuiltInConverter(typeToConvert);
 173
 174            // Expand if factory converter & validate.
 4147175            converter = options.ExpandConverterFactory(converter, typeToConvert);
 4147176            if (!converter.Type!.IsInSubtypeRelationshipWith(typeToConvert))
 0177            {
 0178                ThrowHelper.ThrowInvalidOperationException_SerializationConverterNotCompatible(converter.GetType(), type
 179            }
 180
 4147181            JsonSerializerOptions.CheckConverterNullabilityIsSameAsPropertyType(converter, typeToConvert);
 4147182            return converter;
 4147183        }
 184
 185        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 186        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 187        private static JsonConverter GetConverterFromAttribute(JsonConverterAttribute converterAttribute, Type typeToCon
 86188        {
 189            JsonConverter? converter;
 190
 86191            Type declaringType = memberInfo?.DeclaringType ?? typeToConvert;
 86192            Type? converterType = converterAttribute.ConverterType;
 86193            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
 86203            {
 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.
 86207                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
 86220                ConstructorInfo? ctor = converterType.GetConstructor(Type.EmptyTypes);
 86221                if (!typeof(JsonConverter).IsAssignableFrom(converterType) || ctor == null || !ctor.IsPublic)
 0222                {
 0223                    ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeInvalid(declaringType, m
 224                }
 225
 86226                converter = (JsonConverter)Activator.CreateInstance(converterType)!;
 86227            }
 228
 86229            Debug.Assert(converter is not null);
 86230            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
 86247            return converter;
 86248        }
 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)]
 131        private DefaultJsonTypeInfoResolver(bool mutable)
 132        {
 133            _mutable = mutable;
 134        }
 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)
 414752        {
 414753            ArgumentNullException.ThrowIfNull(type);
 414754            ArgumentNullException.ThrowIfNull(options);
 55
 414756            _mutable = false;
 57
 414758            JsonTypeInfo.ValidateType(type);
 414759            JsonTypeInfo typeInfo = CreateJsonTypeInfo(type, options);
 414760            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.
 414764            typeInfo.IsCustomized = false;
 65
 414766            if (_modifiers is not null)
 067            {
 068                foreach (Action<JsonTypeInfo> modifier in _modifiers)
 069                {
 070                    modifier(typeInfo);
 071                }
 072            }
 73
 414774            return typeInfo;
 414775        }
 76
 77        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 78        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 79        private static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options)
 414780        {
 414781            JsonConverter converter = GetConverterForType(type, options);
 414782            return CreateTypeInfoCore(type, converter, options);
 414783        }
 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.
 86118            => _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
 283125            {
 283126                if (s_defaultInstance is DefaultJsonTypeInfoResolver result)
 282127                {
 282128                    return result;
 129                }
 130
 1131                var newInstance = new DefaultJsonTypeInfoResolver(mutable: false);
 1132                return Interlocked.CompareExchange(ref s_defaultInstance, newInstance, comparand: null) ?? newInstance;
 283133            }
 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
 1043220            {
 1043221                return global::System.Text.Json.Serialization.Metadata.MemberAccessor.Instance;
 1043222            }
 23        }
 24
 25        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 26        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 27        private static JsonTypeInfo CreateTypeInfoCore(Type type, JsonConverter converter, JsonSerializerOptions options
 414728        {
 414729            JsonTypeInfo typeInfo = JsonTypeInfo.CreateJsonTypeInfo(type, converter, options);
 30
 414731            if (GetNumberHandlingForType(typeInfo.Type) is { } numberHandling)
 032            {
 033                typeInfo.NumberHandling = numberHandling;
 034            }
 35
 414736            if (GetObjectCreationHandlingForType(typeInfo.Type) is { } creationHandling)
 037            {
 038                typeInfo.PreferredPropertyObjectCreationHandling = creationHandling;
 039            }
 40
 414741            if (GetUnmappedMemberHandling(typeInfo.Type) is { } unmappedMemberHandling
 414742                && typeInfo.Kind is JsonTypeInfoKind.Object)
 043            {
 044                typeInfo.UnmappedMemberHandling = unmappedMemberHandling;
 045            }
 46
 414747            PopulatePolymorphismMetadata(typeInfo);
 48
 414749            typeInfo.MapInterfaceTypesToCallbacks();
 50
 414751            Func<object>? createObject = DetermineCreateObjectDelegate(type, converter);
 414752            typeInfo.SetCreateObjectIfCompatible(createObject);
 414753            typeInfo.CreateObjectForExtensionDataProperty = createObject;
 54
 414755            if (typeInfo is { Kind: JsonTypeInfoKind.Object, IsNullable: false })
 49656            {
 49657                NullabilityInfoContext nullabilityCtx = new();
 58
 49659                if (converter.ConstructorIsParameterized)
 33360                {
 61                    // NB parameter metadata must be populated *before* property metadata
 62                    // so that properties can be linked to their associated parameters.
 33363                    PopulateParameterInfoValues(typeInfo, nullabilityCtx);
 33364                }
 65
 49666                PopulateProperties(typeInfo, nullabilityCtx);
 67
 49668                typeInfo.ConstructorAttributeProvider = typeInfo.Converter.ConstructorInfo;
 49669            }
 70
 414771            if (typeInfo.Kind is JsonTypeInfoKind.Union)
 072            {
 073                PopulateUnionMetadata(typeInfo);
 074            }
 75
 76            // Plug in any converter configuration -- should be run last.
 414777            converter.ConfigureJsonTypeInfo(typeInfo, options);
 414778            converter.ConfigureJsonTypeInfoUsingReflection(typeInfo, options);
 414779            return typeInfo;
 414780        }
 81
 82        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 83        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 84        internal static void PopulatePolymorphismMetadata(JsonTypeInfo typeInfo)
 414785        {
 414786            Debug.Assert(!typeInfo.IsReadOnly);
 87
 414788            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.
 414799            bool? inferClosedTypePolymorphismOverride = polymorphicAttribute?.InferClosedTypePolymorphismOrNull;
 100
 4147101            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.
 4147106            bool isClosedType = false;
 4147107            Type[]? closedDerivedTypes = null;
 108
 4147109            if (inferClosedTypePolymorphismOverride is true ||
 4147110                (inferClosedTypePolymorphism && options is null or { DerivedTypes.Count: 0 }))
 0111            {
 0112                isClosedType = IsClosedType(typeInfo.Type, out closedDerivedTypes);
 0113            }
 114
 4147115            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
 4147124            if (inferClosedTypePolymorphism &&
 4147125                options is null or { DerivedTypes.Count: 0 } &&
 4147126                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            }
 4147146            else if (inferClosedTypePolymorphismOverride is false &&
 4147147                options is { DerivedTypes.Count: 0 } &&
 4147148                polymorphicAttribute is not null &&
 4147149                !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
 4147162            if (options is not null)
 0163            {
 0164                ResolveOpenGenericDerivedTypes(typeInfo.Type, options.DerivedTypes);
 0165                typeInfo.SetPolymorphismOptions(options);
 0166            }
 167
 4147168            if (typeInfo.Kind is not JsonTypeInfoKind.Union)
 4147169            {
 4147170                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
 4147180                if (typeInfo.PolymorphismOptions is not null &&
 4147181                    (typeInfo.TypeClassifierFactory is not null || typeInfo.Options.TypeClassifiers.Count > 0))
 0182                {
 0183                    typeInfo.TypeClassifierResolutionPending = true;
 0184                }
 4147185            }
 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;
 4147199        }
 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)
 496540        {
 496541            Debug.Assert(!typeInfo.IsReadOnly);
 496542            Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object);
 543
 544            // SetsRequiredMembersAttribute means that all required members are assigned by constructor and therefore th
 496545            bool constructorHasSetsRequiredMembersAttribute =
 496546                typeInfo.Converter.ConstructorInfo?.HasSetsRequiredMembersAttribute() ?? false;
 547
 548            // Resolve the type-level JsonNamingPolicyAttribute once for the entire type.
 496549            JsonNamingPolicy? typeNamingPolicy = typeInfo.Type.GetUniqueCustomAttribute<JsonNamingPolicyAttribute>(inher
 550
 551            // Resolve type-level [JsonIgnore] once per type, rather than per-member.
 496552            JsonIgnoreCondition? typeIgnoreCondition = typeInfo.Type.GetUniqueCustomAttribute<JsonIgnoreAttribute>(inher
 496553            if (typeIgnoreCondition == JsonIgnoreCondition.Always)
 0554            {
 0555                ThrowHelper.ThrowInvalidOperationException(SR.DefaultIgnoreConditionInvalid);
 556            }
 557
 496558            JsonTypeInfo.PropertyHierarchyResolutionState state = new(typeInfo.Options);
 559
 560            // Walk the type hierarchy starting from the current type up to the base type(s)
 2976561            foreach (Type currentType in typeInfo.Type.GetSortedTypeHierarchy())
 992562            {
 992563                if (currentType == typeof(object) ||
 992564                    currentType == typeof(ValueType))
 496565                {
 566                    // Don't process any members for typeof(object) or System.ValueType
 496567                    break;
 568                }
 569
 496570                AddMembersDeclaredBySuperType(
 496571                    typeInfo,
 496572                    currentType,
 496573                    typeNamingPolicy,
 496574                    nullabilityCtx,
 496575                    typeIgnoreCondition,
 496576                    constructorHasSetsRequiredMembersAttribute,
 496577                    ref state);
 496578            }
 579
 496580            if (state.IsPropertyOrderSpecified)
 0581            {
 0582                typeInfo.PropertyList.SortProperties();
 0583            }
 496584        }
 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)
 496603        {
 496604            Debug.Assert(!typeInfo.IsReadOnly);
 496605            Debug.Assert(currentType.IsAssignableFrom(typeInfo.Type));
 606
 607            // Compiler adds RequiredMemberAttribute to type if any of the members are marked with 'required' keyword.
 496608            bool shouldCheckMembersForRequiredMemberAttribute =
 496609                !constructorHasSetsRequiredMembersAttribute && currentType.HasRequiredMemberAttribute();
 610
 8106611            foreach (PropertyInfo propertyInfo in currentType.GetProperties(AllInstanceMembers))
 3309612            {
 613                // Ignore indexers and virtual properties that have overrides that were [JsonIgnore]d.
 3309614                if (propertyInfo.GetIndexParameters().Length > 0 ||
 3309615                    PropertyIsOverriddenAndIgnored(propertyInfo, state.IgnoredProperties))
 0616                {
 0617                    continue;
 618                }
 619
 3309620                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
 3309623                if (propertyInfo.GetMethod?.IsPublic == true ||
 3309624                    propertyInfo.SetMethod?.IsPublic == true ||
 3309625                    hasJsonIncludeAttribute)
 2976626                {
 2976627                    AddMember(
 2976628                        typeInfo,
 2976629                        typeToConvert: propertyInfo.PropertyType,
 2976630                        memberInfo: propertyInfo,
 2976631                        typeNamingPolicy,
 2976632                        nullabilityCtx,
 2976633                        typeIgnoreCondition,
 2976634                        shouldCheckMembersForRequiredMemberAttribute,
 2976635                        hasJsonIncludeAttribute,
 2976636                        ref state);
 2976637                }
 3309638            }
 639
 7440640            foreach (FieldInfo fieldInfo in currentType.GetFields(AllInstanceMembers))
 2976641            {
 2976642                bool hasJsonIncludeAttribute = fieldInfo.GetCustomAttribute<JsonIncludeAttribute>(inherit: false) is not
 2976643                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                }
 2976656            }
 496657        }
 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)
 2976671        {
 2976672            JsonPropertyInfo? jsonPropertyInfo = CreatePropertyInfo(typeInfo, typeToConvert, memberInfo, typeNamingPolic
 2976673            if (jsonPropertyInfo is null)
 0674            {
 675                // ignored invalid property
 0676                return;
 677            }
 678
 2976679            Debug.Assert(jsonPropertyInfo.Name is not null);
 2976680            typeInfo.PropertyList.AddPropertyWithConflictResolution(jsonPropertyInfo, ref state);
 2976681        }
 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)
 2976695        {
 2976696            JsonIgnoreCondition? ignoreCondition = memberInfo.GetCustomAttribute<JsonIgnoreAttribute>(inherit: false)?.C
 697
 698            // Fall back to the type-level [JsonIgnore] if no member-level attribute is specified.
 2976699            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
 2976708            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
 2976719            {
 2976720                customConverter = GetCustomConverterForMember(typeToConvert, memberInfo, options);
 2976721            }
 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
 2976728            JsonPropertyInfo jsonPropertyInfo = typeInfo.CreatePropertyUsingReflection(typeToConvert, declaringType: mem
 2976729            PopulatePropertyInfo(jsonPropertyInfo, memberInfo, customConverter, ignoreCondition, nullabilityCtx, shouldC
 2976730            return jsonPropertyInfo;
 2976731        }
 732
 733        private static JsonNumberHandling? GetNumberHandlingForType(Type type)
 4147734        {
 4147735            JsonNumberHandlingAttribute? numberHandlingAttribute = type.GetUniqueCustomAttribute<JsonNumberHandlingAttri
 4147736            return numberHandlingAttribute?.Handling;
 4147737        }
 738
 739        private static JsonObjectCreationHandling? GetObjectCreationHandlingForType(Type type)
 4147740        {
 4147741            JsonObjectCreationHandlingAttribute? creationHandlingAttribute = type.GetUniqueCustomAttribute<JsonObjectCre
 4147742            return creationHandlingAttribute?.Handling;
 4147743        }
 744
 745        private static JsonUnmappedMemberHandling? GetUnmappedMemberHandling(Type type)
 4147746        {
 4147747            JsonUnmappedMemberHandlingAttribute? numberHandlingAttribute = type.GetUniqueCustomAttribute<JsonUnmappedMem
 4147748            return numberHandlingAttribute?.UnmappedMemberHandling;
 4147749        }
 750
 751        private static bool PropertyIsOverriddenAndIgnored(PropertyInfo propertyInfo, Dictionary<string, JsonPropertyInf
 3309752        {
 3309753            return propertyInfo.IsVirtual() &&
 3309754                ignoredMembers?.TryGetValue(propertyInfo.Name, out JsonPropertyInfo? ignoredMember) == true &&
 3309755                ignoredMember.IsVirtual &&
 3309756                propertyInfo.PropertyType == ignoredMember.PropertyType;
 3309757        }
 758
 759        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 760        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 761        private static void PopulateParameterInfoValues(JsonTypeInfo typeInfo, NullabilityInfoContext nullabilityCtx)
 333762        {
 333763            Debug.Assert(typeInfo.Converter.ConstructorInfo != null);
 333764            ParameterInfo[] parameters = typeInfo.Converter.ConstructorInfo.GetParameters();
 765
 766            // Count non-out parameters - out parameters don't receive values from JSON.
 333767            int nonOutParameterCount = 0;
 4995768            foreach (ParameterInfo param in parameters)
 1998769            {
 1998770                if (!param.IsOut)
 1998771                {
 1998772                    nonOutParameterCount++;
 1998773                }
 1998774            }
 775
 333776            JsonParameterInfoValues[] jsonParameters = new JsonParameterInfoValues[nonOutParameterCount];
 777
 333778            int jsonParamIndex = 0;
 4662779            for (int i = 0; i < parameters.Length; i++)
 1998780            {
 1998781                ParameterInfo reflectionInfo = parameters[i];
 782
 783                // Skip out parameters - they don't receive values from JSON deserialization.
 1998784                if (reflectionInfo.IsOut)
 0785                {
 0786                    continue;
 787                }
 788
 789                // Trimmed parameter names are reported as null in CoreCLR or "" in Mono.
 1998790                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.
 1998797                Type parameterType = reflectionInfo.ParameterType;
 1998798                if (parameterType.IsByRef)
 0799                {
 0800                    parameterType = parameterType.GetElementType()!;
 0801                }
 802
 1998803                JsonParameterInfoValues jsonInfo = new()
 1998804                {
 1998805                    Name = reflectionInfo.Name,
 1998806                    ParameterType = parameterType,
 1998807                    Position = jsonParamIndex, // Use the position in the args array, not the constructor parameter inde
 1998808                    HasDefaultValue = reflectionInfo.HasDefaultValue,
 1998809                    DefaultValue = reflectionInfo.GetDefaultValue(),
 1998810                    IsNullable = DetermineParameterNullability(reflectionInfo, nullabilityCtx) is not NullabilityState.N
 1998811                };
 812
 1998813                jsonParameters[jsonParamIndex] = jsonInfo;
 1998814                jsonParamIndex++;
 1998815            }
 816
 333817            typeInfo.PopulateParameterInfoValues(jsonParameters);
 333818        }
 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)
 2976831        {
 2976832            Debug.Assert(jsonPropertyInfo.AttributeProvider is null);
 833
 2976834            switch (jsonPropertyInfo.AttributeProvider = memberInfo)
 835            {
 836                case PropertyInfo propertyInfo:
 2976837                    jsonPropertyInfo.MemberName = propertyInfo.Name;
 2976838                    jsonPropertyInfo.IsVirtual = propertyInfo.IsVirtual();
 2976839                    jsonPropertyInfo.MemberType = MemberTypes.Property;
 2976840                    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
 2976850            jsonPropertyInfo.CustomConverter = customConverter;
 2976851            DeterminePropertyPolicies(jsonPropertyInfo, memberInfo);
 2976852            DeterminePropertyName(jsonPropertyInfo, memberInfo, typeNamingPolicy);
 2976853            DeterminePropertyIsRequired(jsonPropertyInfo, memberInfo, shouldCheckForRequiredKeyword);
 2976854            DeterminePropertyNullability(jsonPropertyInfo, memberInfo, nullabilityCtx);
 855
 2976856            if (ignoreCondition != JsonIgnoreCondition.Always)
 2976857            {
 2976858                jsonPropertyInfo.DetermineReflectionPropertyAccessors(memberInfo, useNonPublicAccessors: hasJsonIncludeA
 2976859            }
 860
 2976861            jsonPropertyInfo.IgnoreCondition = ignoreCondition;
 2976862            jsonPropertyInfo.IsExtensionData = memberInfo.GetCustomAttribute<JsonExtensionDataAttribute>(inherit: false)
 2976863        }
 864
 865        private static void DeterminePropertyPolicies(JsonPropertyInfo propertyInfo, MemberInfo memberInfo)
 2976866        {
 2976867            JsonPropertyOrderAttribute? orderAttr = memberInfo.GetCustomAttribute<JsonPropertyOrderAttribute>(inherit: f
 2976868            propertyInfo.Order = orderAttr?.Order ?? 0;
 869
 2976870            JsonNumberHandlingAttribute? numberHandlingAttr = memberInfo.GetCustomAttribute<JsonNumberHandlingAttribute>
 2976871            propertyInfo.NumberHandling = numberHandlingAttr?.Handling;
 872
 2976873            JsonObjectCreationHandlingAttribute? objectCreationHandlingAttr = memberInfo.GetCustomAttribute<JsonObjectCr
 2976874            propertyInfo.ObjectCreationHandling = objectCreationHandlingAttr?.Handling;
 2976875        }
 876
 877        private static void DeterminePropertyName(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, JsonNamingPolicy
 2976878        {
 2976879            JsonPropertyNameAttribute? nameAttribute = memberInfo.GetCustomAttribute<JsonPropertyNameAttribute>(inherit:
 880            string? name;
 2976881            if (nameAttribute is not null)
 0882            {
 0883                name = nameAttribute.Name;
 0884            }
 885            else
 2976886            {
 2976887                JsonNamingPolicy? effectivePolicy = memberInfo.GetCustomAttribute<JsonNamingPolicyAttribute>(inherit: fa
 2976888                    ?? typeNamingPolicy
 2976889                    ?? propertyInfo.Options.PropertyNamingPolicy;
 890
 2976891                name = effectivePolicy is not null
 2976892                    ? effectivePolicy.ConvertName(memberInfo.Name)
 2976893                    : memberInfo.Name;
 2976894            }
 895
 2976896            if (name is null)
 0897            {
 0898                ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameNull(propertyInfo);
 899            }
 900
 2976901            propertyInfo.Name = name;
 2976902        }
 903
 904        private static void DeterminePropertyIsRequired(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, bool shoul
 2976905        {
 2976906            propertyInfo.IsRequired =
 2976907                memberInfo.GetCustomAttribute<JsonRequiredAttribute>(inherit: false) is not null
 2976908                || (shouldCheckForRequiredKeyword && memberInfo.HasRequiredMemberAttribute());
 2976909        }
 910
 911        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 912        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 913        internal static void DeterminePropertyAccessors<T>(JsonPropertyInfo<T> jsonPropertyInfo, MemberInfo memberInfo, 
 2976914        {
 2976915            Debug.Assert(memberInfo is FieldInfo or PropertyInfo);
 916
 2976917            switch (memberInfo)
 918            {
 919                case PropertyInfo propertyInfo:
 2976920                    MethodInfo? getMethod = propertyInfo.GetMethod;
 2976921                    if (getMethod != null && (getMethod.IsPublic || useNonPublicAccessors))
 2976922                    {
 2976923                        jsonPropertyInfo.Get = MemberAccessor.CreatePropertyGetter<T>(propertyInfo);
 2976924                    }
 925
 2976926                    MethodInfo? setMethod = propertyInfo.SetMethod;
 2976927                    if (setMethod != null && (setMethod.IsPublic || useNonPublicAccessors))
 2976928                    {
 2976929                        jsonPropertyInfo.Set = MemberAccessor.CreatePropertySetter<T>(propertyInfo);
 2976930                    }
 931
 2976932                    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            }
 2976950        }
 951
 952        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 953        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 954        private static Func<object>? DetermineCreateObjectDelegate(Type type, JsonConverter converter)
 4147955        {
 4147956            ConstructorInfo? defaultCtor = null;
 957
 4147958            if (converter.ConstructorInfo != null && !converter.ConstructorIsParameterized)
 163959            {
 960                // A parameterless constructor has been resolved by the converter
 961                // (e.g. it might be a non-public ctor with JsonConstructorAttribute).
 163962                defaultCtor = converter.ConstructorInfo;
 163963            }
 964
 965            // Fall back to resolving any public constructors on the type.
 4147966            defaultCtor ??= type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, binder: null, Type.EmptyTyp
 967
 4147968            return MemberAccessor.CreateParameterlessConstructor(type, defaultCtor);
 4147969        }
 970
 971        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 972        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 973        private static void DeterminePropertyNullability(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, Nullabili
 2976974        {
 2976975            if (!propertyInfo.PropertyTypeCanBeNull)
 2082976            {
 2082977                return;
 978            }
 979
 980            NullabilityInfo nullabilityInfo;
 894981            if (propertyInfo.MemberType is MemberTypes.Property)
 894982            {
 894983                nullabilityInfo = nullabilityCtx.Create((PropertyInfo)memberInfo);
 894984            }
 985            else
 0986            {
 0987                Debug.Assert(propertyInfo.MemberType is MemberTypes.Field);
 0988                nullabilityInfo = nullabilityCtx.Create((FieldInfo)memberInfo);
 0989            }
 990
 894991            propertyInfo.IsGetNullable = nullabilityInfo.ReadState is not NullabilityState.NotNull;
 894992            propertyInfo.IsSetNullable = nullabilityInfo.WriteState is not NullabilityState.NotNull;
 2976993        }
 994
 995        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 996        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 997        private static NullabilityState DetermineParameterNullability(ParameterInfo parameterInfo, NullabilityInfoContex
 1998998        {
 1998999            if (!parameterInfo.ParameterType.IsNullableType())
 13021000            {
 13021001                return NullabilityState.NotNull;
 1002            }
 1003
 6961004            NullabilityInfo nullability = nullabilityCtx.Create(parameterInfo);
 6961005            return nullability.WriteState;
 19981006        }
 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&)