File: System\Text\Json\Serialization\Converters\Union\JsonUnionConverterFactory.cs
Web Access
Project: src\src\runtime\src\libraries\System.Text.Json\src\System.Text.Json.csproj (System.Text.Json)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using System.Reflection;

namespace System.Text.Json.Serialization.Converters
{
    [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
    [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
    internal sealed class JsonUnionConverterFactory : JsonConverterFactory
    {
        private const string UnionAttributeName = "UnionAttribute";
        private const string UnionAttributeFullName = "System.Runtime.CompilerServices.UnionAttribute";

        // A type IS a union iff it carries [System.Runtime.CompilerServices.UnionAttribute].
        // The marker is detected by full name (rather than via a compile-time reference)
        // because the C# compiler may polyfill it into the user's assembly when targeting a
        // downlevel runtime. [JsonUnion] alone is a customization attribute that has no meaning
        // on non-union types.
        public override bool CanConvert(Type typeToConvert) => IsUnionType(typeToConvert);

        public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
        {
            Type converterType = typeof(JsonUnionConverter<>).MakeGenericType(typeToConvert);
            return (JsonConverter)Activator.CreateInstance(converterType)!;
        }

        private static bool IsUnionType(Type type)
        {
            foreach (CustomAttributeData data in type.GetCustomAttributesData())
            {
                Type attributeType = data.AttributeType;
                if (attributeType.Name == UnionAttributeName &&
                    attributeType.FullName == UnionAttributeFullName)
                {
                    return true;
                }
            }

            return false;
        }
    }
}