diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Converters/FuncExpressionValueConverter.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/FuncExpressionValueConverter.cs new file mode 100644 index 000000000..ad13c2bf7 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/FuncExpressionValueConverter.cs @@ -0,0 +1,28 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Elsa.Expressions.Models; + +namespace Elsa.Workflows.Serialization.Converters; + +/// +/// Prevents System.Text.Json from trying to serialize the compiled delegate. +/// Always emits null and cannot rehydrate a Func. +/// +public class FuncExpressionValueConverter : JsonConverter>> +{ + public override Func> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Skip whatever value is in the JSON (probably null). + if (reader.TokenType != JsonTokenType.Null) + reader.Skip(); + + // We can't deserialize a delegate, so return null. + return null!; + } + + public override void Write(Utf8JsonWriter writer, Func> value, JsonSerializerOptions options) + { + // Emit a JSON null instead of trying to serialize the delegate + writer.WriteNullValue(); + } +} diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Converters/FuncExpressionValueConverterFactory.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/FuncExpressionValueConverterFactory.cs new file mode 100644 index 000000000..0b7cd10b0 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/FuncExpressionValueConverterFactory.cs @@ -0,0 +1,12 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Elsa.Expressions.Models; + +namespace Elsa.Workflows.Serialization.Converters; + +public class FuncExpressionConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type t) => t == typeof(Func>); + + public override JsonConverter CreateConverter(Type t, JsonSerializerOptions opts) => new FuncExpressionValueConverter(); +} diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/ApiSerializer.cs b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/ApiSerializer.cs index a3423338a..7f97058db 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/ApiSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/ApiSerializer.cs @@ -10,9 +10,8 @@ public class ApiSerializer : ConfigurableSerializer, IApiSerializer /// /// Initializes a new instance of the class. /// - public ApiSerializer(IServiceProvider serviceProvider) : base(serviceProvider) - { - } + public ApiSerializer(IServiceProvider serviceProvider) + : base(serviceProvider) { } /// public string Serialize(object model) @@ -41,6 +40,7 @@ public class ApiSerializer : ConfigurableSerializer, IApiSerializer protected override void AddConverters(JsonSerializerOptions options) { options.Converters.Add(CreateInstance()); + options.Converters.Add(CreateInstance()); } JsonSerializerOptions IApiSerializer.GetOptions() => GetOptions(); @@ -50,4 +50,4 @@ public class ApiSerializer : ConfigurableSerializer, IApiSerializer ApplyOptions(options); return options; } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/BookmarkPayloadSerializer.cs b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/BookmarkPayloadSerializer.cs index 0d6e73f15..9dab96949 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/BookmarkPayloadSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/BookmarkPayloadSerializer.cs @@ -18,18 +18,21 @@ public class BookmarkPayloadSerializer : IBookmarkPayloadSerializer { // Enables serialization of ValueTuples, which use fields instead of properties. IncludeFields = true, - PropertyNameCaseInsensitive = true + PropertyNameCaseInsensitive = true, }; - + _settings.Converters.Add(new TypeJsonConverter(wellKnownTypeRegistry)); + _settings.Converters.Add(new FuncExpressionValueConverter()); } /// - public T Deserialize(string json) where T : notnull => JsonSerializer.Deserialize(json, _settings)!; + public T Deserialize(string json) + where T : notnull => JsonSerializer.Deserialize(json, _settings)!; /// public object Deserialize(string json, Type type) => JsonSerializer.Deserialize(json, type, _settings)!; /// - public string Serialize(T payload) where T : notnull => JsonSerializer.Serialize(payload, payload.GetType(), _settings); -} \ No newline at end of file + public string Serialize(T payload) + where T : notnull => JsonSerializer.Serialize(payload, payload.GetType(), _settings); +} diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonActivitySerializer.cs b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonActivitySerializer.cs index 62f65db9a..7e3f996aa 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonActivitySerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonActivitySerializer.cs @@ -37,5 +37,6 @@ public class JsonActivitySerializer(IServiceProvider serviceProvider) : Configur options.Converters.Add(CreateInstance()); options.Converters.Add(CreateInstance()); options.Converters.Add(CreateInstance()); + options.Converters.Add(CreateInstance()); } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonPayloadSerializer.cs b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonPayloadSerializer.cs index c5ab81a8f..9c97db7a8 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonPayloadSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonPayloadSerializer.cs @@ -20,7 +20,7 @@ public class JsonPayloadSerializer : IPayloadSerializer { _serviceProvider = serviceProvider; } - + /// public string Serialize(object payload) { @@ -82,8 +82,10 @@ public class JsonPayloadSerializer : IPayloadSerializer options.Converters.Add(GetService()); options.Converters.Add(GetService()); options.Converters.Add(GetService()); + options.Converters.Add(new FuncExpressionValueConverter()); return options; } - - private T GetService() where T : notnull => ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider); -} \ No newline at end of file + + private T GetService() + where T : notnull => ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider); +} diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonWorkflowStateSerializer.cs b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonWorkflowStateSerializer.cs index ad664f7e8..faf4215b8 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonWorkflowStateSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonWorkflowStateSerializer.cs @@ -20,7 +20,8 @@ public class JsonWorkflowStateSerializer : ConfigurableSerializer, IWorkflowStat /// /// Initializes a new instance of the class. /// - public JsonWorkflowStateSerializer(IServiceProvider serviceProvider, IWellKnownTypeRegistry wellKnownTypeRegistry, ILoggerFactory loggerFactory) : base(serviceProvider) + public JsonWorkflowStateSerializer(IServiceProvider serviceProvider, IWellKnownTypeRegistry wellKnownTypeRegistry, ILoggerFactory loggerFactory) + : base(serviceProvider) { _wellKnownTypeRegistry = wellKnownTypeRegistry; _loggerFactory = loggerFactory; @@ -128,10 +129,7 @@ public class JsonWorkflowStateSerializer : ConfigurableSerializer, IWorkflowStat public override JsonSerializerOptions GetOptions() { var options = base.GetOptions(); - return new(options) - { - ReferenceHandler = new CrossScopedReferenceHandler() - }; + return new(options) { ReferenceHandler = new CrossScopedReferenceHandler() }; } /// @@ -140,5 +138,6 @@ public class JsonWorkflowStateSerializer : ConfigurableSerializer, IWorkflowStat options.Converters.Add(new TypeJsonConverter(_wellKnownTypeRegistry)); options.Converters.Add(new PolymorphicObjectConverterFactory(_wellKnownTypeRegistry)); options.Converters.Add(new VariableConverterFactory(_wellKnownTypeRegistry, _loggerFactory)); + options.Converters.Add(new FuncExpressionValueConverter()); } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/SafeSerializer.cs b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/SafeSerializer.cs index fbfd58a82..46b7b5400 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/SafeSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/SafeSerializer.cs @@ -13,9 +13,8 @@ namespace Elsa.Workflows.Serialization.Serializers; public class SafeSerializer : ConfigurableSerializer, ISafeSerializer { /// - public SafeSerializer(IServiceProvider serviceProvider) : base(serviceProvider) - { - } + public SafeSerializer(IServiceProvider serviceProvider) + : base(serviceProvider) { } /// [RequiresUnreferencedCode("The type T may be trimmed.")] @@ -78,5 +77,6 @@ public class SafeSerializer : ConfigurableSerializer, ISafeSerializer options.Converters.Add(new TypeJsonConverter(WellKnownTypeRegistry.CreateDefault())); options.Converters.Add(new SafeValueConverterFactory()); options.Converters.Add(new ExpressionJsonConverterFactory(expressionDescriptorRegistry)); + options.Converters.Add(new FuncExpressionValueConverter()); } -} \ No newline at end of file +}