< Summary

Information
Class: Elsa.Workflows.Serialization.Configurators.CustomConstructorConfigurator
Assembly: Elsa.Workflows.Core
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Serialization/Configurators/CustomConstructorConfigurator.cs
Line coverage
100%
Covered lines: 26
Uncovered lines: 0
Coverable lines: 26
Total lines: 65
Line coverage: 100%
Branch coverage
100%
Covered branches: 30
Total branches: 30
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GetModifiers()100%11100%
GetDefaultConstructor(...)100%2222100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Serialization/Configurators/CustomConstructorConfigurator.cs

#LineLine coverage
 1using System.Diagnostics.CodeAnalysis;
 2using System.Reflection;
 3using System.Runtime.CompilerServices;
 4using System.Text.Json.Serialization;
 5using System.Text.Json.Serialization.Metadata;
 6
 7namespace Elsa.Workflows.Serialization.Configurators;
 8
 9/// <summary>
 10/// Configures the contract resolver to add support for using non-default, private constructors for deserialization.
 11/// </summary>
 12public class CustomConstructorConfigurator : SerializationOptionsConfiguratorBase
 13{
 14    /// <inheritdoc />
 15    public override IEnumerable<Action<JsonTypeInfo>> GetModifiers()
 16    {
 17        // Set the default constructor for all types that have a default constructor.
 77118        yield return jsonTypeInfo =>
 77119        {
 15829820            if (jsonTypeInfo is not { Kind: JsonTypeInfoKind.Object, CreateObject: null })
 7258521                return;
 77122
 8571323            var defaultConstructor = GetDefaultConstructor(jsonTypeInfo.Type);
 8571324            if (defaultConstructor != null)
 77125            {
 405126                jsonTypeInfo.CreateObject = defaultConstructor;
 77127            }
 8648428        };
 77129    }
 30
 31    private static Func<object>? GetDefaultConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Public
 32    {
 18766133        foreach (var constructor in type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Ins
 34        {
 35            // If we have a default constructor, use that one.
 1014336            if (!constructor.GetParameters().Any())
 256537                return () => constructor.Invoke(default, [])!;
 38
 39            // Else, find a constructor with the following signature: (string?, int?).
 40            // Check for a constructor with the following signature:
 41            // ctor(string, int) where string is decorated with [CallerFilePath] and int is decorated with [CallerLineNu
 757842            var parameters = constructor.GetParameters();
 43
 44            // Check parameter count
 757845            if (parameters.Length != 2) continue;
 46
 47            // Does the constructor have a [JsonConstructor] attribute?
 588248            var isJsonConstructor = constructor.GetCustomAttribute<JsonConstructorAttribute>() != null;
 49
 50            // Check first parameter type and attribute
 588251            if (parameters[0].ParameterType != typeof(string) ||
 588252                parameters[0].DefaultValue != null ||
 588253                (parameters[0].GetCustomAttribute<CallerFilePathAttribute>() == null && !isJsonConstructor)) continue;
 54
 55            // Check second parameter type and attribute
 148656            if (parameters[1].ParameterType != typeof(int?) ||
 148657                parameters[1].DefaultValue != null ||
 148658                (parameters[1].GetCustomAttribute<CallerLineNumberAttribute>() == null && !isJsonConstructor)) continue;
 59
 250760            return () => constructor.Invoke([null!, 0]);
 61        }
 62
 8166263        return null;
 64    }
 65}