< Summary

Information
Class: Elsa.Workflows.Serialization.Converters.ExcludeFromHashConverter
Assembly: Elsa.Workflows.Core
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Serialization/Converters/ExcludeFromHashConverter.cs
Line coverage
84%
Covered lines: 38
Uncovered lines: 7
Coverable lines: 45
Total lines: 123
Line coverage: 84.4%
Branch coverage
62%
Covered branches: 18
Total branches: 29
Branch coverage: 62%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
Read(...)100%210%
Write(...)100%44100%
GetSerializableProperties(...)83.33%66100%
GetPublicInstanceProperties()100%66100%
ShouldAlwaysIgnoreProperty(...)100%11100%
ShouldIgnoreProperty(...)11.11%23944.44%
IsDefaultValue(...)0%620%
get_Property()100%11100%
GetClonedOptions(...)100%22100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Serialization/Converters/ExcludeFromHashConverter.cs

#LineLine coverage
 1using System.Reflection;
 2using System.Runtime.CompilerServices;
 3using System.Text.Json;
 4using System.Text.Json.Serialization;
 5using Elsa.Extensions;
 6using Elsa.Workflows.Attributes;
 7
 8namespace Elsa.Workflows.Serialization.Converters;
 9
 10/// <summary>
 11/// Serializes an object to JSON, excluding properties marked with <see cref="ExcludeFromHashAttribute"/>.
 12/// Properties ignored by <see cref="JsonIgnoreAttribute"/> are also excluded according to their configured ignore condi
 13/// avoid adding or changing these attributes on bookmark or stimulus payloads whose hashes must remain compatible with 
 14/// </summary>
 15public class ExcludeFromHashConverter : JsonConverter<object>
 16{
 117    private static readonly ConditionalWeakTable<Type, PropertyMetadata[]> PropertyCache = new();
 18    private JsonSerializerOptions? _options;
 19
 20    /// <inheritdoc />
 21    public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 22    {
 023        throw new NotSupportedException();
 24    }
 25
 26    /// <inheritdoc />
 27    public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
 28    {
 1224229        writer.WriteStartObject();
 1224230        var newOptions = GetClonedOptions(options);
 31
 33676632        foreach (var metadata in GetSerializableProperties(value.GetType()))
 33        {
 15614134            var property = metadata.Property;
 15614135            var propertyValue = property.GetValue(value);
 36
 15614137            if (ShouldIgnoreProperty(metadata.JsonIgnoreCondition, property.PropertyType, propertyValue))
 38            {
 39                continue;
 40            }
 41
 15614142            writer.WritePropertyName(property.Name);
 15614143            JsonSerializer.Serialize(writer, propertyValue, newOptions);
 44        }
 45
 1224246        writer.WriteEndObject();
 1224247    }
 48
 49    private static PropertyMetadata[] GetSerializableProperties(Type type)
 50    {
 1225751        return PropertyCache.GetValue(type, static itemType => GetPublicInstanceProperties(itemType)
 4952            .Where(property => property.GetIndexParameters().Length == 0)
 4953            .Select(property => new
 4954            {
 4955                Property = property,
 4956                ExcludeFromHash = property.GetCustomAttribute<ExcludeFromHashAttribute>(),
 4957                JsonIgnore = property.GetCustomAttribute<JsonIgnoreAttribute>()
 4958            })
 4959            .Where(x => x.ExcludeFromHash == null && !ShouldAlwaysIgnoreProperty(x.JsonIgnore?.Condition))
 4260            .Select(x => new PropertyMetadata(x.Property, x.JsonIgnore?.Condition))
 1225761            .ToArray());
 62    }
 63
 64    private static IEnumerable<PropertyInfo> GetPublicInstanceProperties(Type type)
 65    {
 6466        for (var currentType = type; currentType != null && currentType != typeof(object); currentType = currentType.Bas
 67        {
 13268            foreach (var property in currentType
 1769                         .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)
 70                         .OrderBy(x => x.MetadataToken))
 71            {
 4972                yield return property;
 73            }
 74        }
 1575    }
 76
 77    private static bool ShouldAlwaysIgnoreProperty(JsonIgnoreCondition? condition)
 78    {
 4479        return condition == JsonIgnoreCondition.Always;
 80    }
 81
 82    private static bool ShouldIgnoreProperty(JsonIgnoreCondition? condition, Type declaredType, object? value)
 83    {
 15614184        return condition switch
 15614185        {
 15614186            null => false,
 087            JsonIgnoreCondition.Never => false,
 088            JsonIgnoreCondition.Always => true,
 089            JsonIgnoreCondition.WhenWritingNull => value == null,
 090            JsonIgnoreCondition.WhenWritingDefault => value == null || IsDefaultValue(declaredType, value),
 091            _ => false
 15614192        };
 93    }
 94
 95    private static bool IsDefaultValue(Type declaredType, object value)
 96    {
 097        return declaredType.IsValueType && value.Equals(Activator.CreateInstance(declaredType));
 98    }
 99
 312324100    private sealed record PropertyMetadata(PropertyInfo Property, JsonIgnoreCondition? JsonIgnoreCondition);
 101
 102    private JsonSerializerOptions GetClonedOptions(JsonSerializerOptions options)
 103    {
 12242104        if(_options != null)
 12217105            return _options;
 106
 25107        var newOptions = new JsonSerializerOptions(options);
 75108        newOptions.Converters.RemoveWhere(x => x is ExcludeFromHashConverterFactory);
 25109        return _options = newOptions;
 110    }
 111}
 112
 113/// <summary>
 114/// A factory for creating <see cref="ExcludeFromHashConverter"/> instances.
 115/// </summary>
 116public class ExcludeFromHashConverterFactory : JsonConverterFactory
 117{
 118    /// <inheritdoc />
 119    public override bool CanConvert(Type typeToConvert) => true;
 120
 121    /// <inheritdoc />
 122    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new ExcludeFromH
 123}