File: System\Text\Json\Serialization\Converters\FSharp\FSharpTypeConverterFactory.cs
Web Access
Project: src\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;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization.Metadata;
using FSharpKind = System.Text.Json.Serialization.Metadata.FSharpCoreReflectionProxy.FSharpKind;
 
namespace System.Text.Json.Serialization.Converters
{
    [RequiresDynamicCode(FSharpCoreReflectionProxy.FSharpCoreUnreferencedCodeMessage)]
    internal sealed class FSharpTypeConverterFactory : JsonConverterFactory
    {
        [RequiresUnreferencedCode(FSharpCoreReflectionProxy.FSharpCoreUnreferencedCodeMessage)]
        public FSharpTypeConverterFactory() { }
 
        private ObjectConverterFactory? _recordConverterFactory;
 
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "The ctor is marked RequiresUnreferencedCode.")]
        public override bool CanConvert(Type typeToConvert) =>
            FSharpCoreReflectionProxy.IsFSharpType(typeToConvert) &&
                FSharpCoreReflectionProxy.Instance.DetectFSharpKind(typeToConvert) is not FSharpKind.Unrecognized;
 
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "The ctor is marked RequiresUnreferencedCode.")]
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:MakeGenericType",
            Justification = "The ctor is marked RequiresUnreferencedCode.")]
        public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
        {
            Debug.Assert(CanConvert(typeToConvert));
 
            Type elementType;
            Type converterFactoryType;
            object?[]? constructorArguments = null;
 
            switch (FSharpCoreReflectionProxy.Instance.DetectFSharpKind(typeToConvert))
            {
                case FSharpKind.Option:
                    elementType = typeToConvert.GetGenericArguments()[0];
                    converterFactoryType = typeof(FSharpOptionConverter<,>).MakeGenericType(typeToConvert, elementType);
                    constructorArguments = new object[] { options.GetConverterInternal(elementType) };
                    break;
                case FSharpKind.ValueOption:
                    elementType = typeToConvert.GetGenericArguments()[0];
                    converterFactoryType = typeof(FSharpValueOptionConverter<,>).MakeGenericType(typeToConvert, elementType);
                    constructorArguments = new object[] { options.GetConverterInternal(elementType) };
                    break;
                case FSharpKind.List:
                    elementType = typeToConvert.GetGenericArguments()[0];
                    converterFactoryType = typeof(FSharpListConverter<,>).MakeGenericType(typeToConvert, elementType);
                    break;
                case FSharpKind.Set:
                    elementType = typeToConvert.GetGenericArguments()[0];
                    converterFactoryType = typeof(FSharpSetConverter<,>).MakeGenericType(typeToConvert, elementType);
                    break;
                case FSharpKind.Map:
                    Type[] genericArgs = typeToConvert.GetGenericArguments();
                    Type keyType = genericArgs[0];
                    Type valueType = genericArgs[1];
                    converterFactoryType = typeof(FSharpMapConverter<,,>).MakeGenericType(typeToConvert, keyType, valueType);
                    break;
                case FSharpKind.Record:
                    // Use a modified object converter factory that picks the right constructor for struct record deserialization.
                    ObjectConverterFactory objectFactory = _recordConverterFactory ??= new ObjectConverterFactory(useDefaultConstructorInUnannotatedStructs: false);
                    Debug.Assert(objectFactory.CanConvert(typeToConvert));
                    return objectFactory.CreateConverter(typeToConvert, options);
                case FSharpKind.Union:
                    return UnsupportedTypeConverterFactory.CreateUnsupportedConverterForType(typeToConvert, SR.FSharpDiscriminatedUnionsNotSupported);
                default:
                    Debug.Fail("Unrecognized F# type.");
                    throw new Exception();
            }
 
            return (JsonConverter)Activator.CreateInstance(converterFactoryType, constructorArguments)!;
        }
    }
}