Add FuncExpressionValueConverter for JSON serialization (#6607)
Implemented FuncExpressionValueConverter to handle serialization and deserialization of Func<ExpressionExecutionContext, ValueTask<object>> types, ensuring delegates are not serialized and cannot be rehydrated from JSON. Updated multiple serializers including ApiSerializer, BookmarkPayloadSerializer, JsonActivitySerializer, JsonPayloadSerializer, JsonWorkflowStateSerializer, and SafeSerializer to utilize the new converter in their JSON serialization options. Co-authored-by: Max Brooks <Max@compyl.com>
This commit is contained in:
parent
e81b9f58b5
commit
7120f9f2a0
|
|
@ -0,0 +1,28 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Expressions.Models;
|
||||
|
||||
namespace Elsa.Workflows.Serialization.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Prevents System.Text.Json from trying to serialize the compiled delegate.
|
||||
/// Always emits null and cannot rehydrate a Func.
|
||||
/// </summary>
|
||||
public class FuncExpressionValueConverter : JsonConverter<Func<ExpressionExecutionContext, ValueTask<object>>>
|
||||
{
|
||||
public override Func<ExpressionExecutionContext, ValueTask<object>> 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<ExpressionExecutionContext, ValueTask<object>> value, JsonSerializerOptions options)
|
||||
{
|
||||
// Emit a JSON null instead of trying to serialize the delegate
|
||||
writer.WriteNullValue();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ExpressionExecutionContext, ValueTask<object>>);
|
||||
|
||||
public override JsonConverter CreateConverter(Type t, JsonSerializerOptions opts) => new FuncExpressionValueConverter();
|
||||
}
|
||||
|
|
@ -10,9 +10,8 @@ public class ApiSerializer : ConfigurableSerializer, IApiSerializer
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApiSerializer"/> class.
|
||||
/// </summary>
|
||||
public ApiSerializer(IServiceProvider serviceProvider) : base(serviceProvider)
|
||||
{
|
||||
}
|
||||
public ApiSerializer(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Serialize(object model)
|
||||
|
|
@ -41,6 +40,7 @@ public class ApiSerializer : ConfigurableSerializer, IApiSerializer
|
|||
protected override void AddConverters(JsonSerializerOptions options)
|
||||
{
|
||||
options.Converters.Add(CreateInstance<TypeJsonConverter>());
|
||||
options.Converters.Add(CreateInstance<FuncExpressionValueConverter>());
|
||||
}
|
||||
|
||||
JsonSerializerOptions IApiSerializer.GetOptions() => GetOptions();
|
||||
|
|
@ -50,4 +50,4 @@ public class ApiSerializer : ConfigurableSerializer, IApiSerializer
|
|||
ApplyOptions(options);
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T Deserialize<T>(string json) where T : notnull => JsonSerializer.Deserialize<T>(json, _settings)!;
|
||||
public T Deserialize<T>(string json)
|
||||
where T : notnull => JsonSerializer.Deserialize<T>(json, _settings)!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Deserialize(string json, Type type) => JsonSerializer.Deserialize(json, type, _settings)!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Serialize<T>(T payload) where T : notnull => JsonSerializer.Serialize(payload, payload.GetType(), _settings);
|
||||
}
|
||||
public string Serialize<T>(T payload)
|
||||
where T : notnull => JsonSerializer.Serialize(payload, payload.GetType(), _settings);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,5 +37,6 @@ public class JsonActivitySerializer(IServiceProvider serviceProvider) : Configur
|
|||
options.Converters.Add(CreateInstance<InputJsonConverterFactory>());
|
||||
options.Converters.Add(CreateInstance<OutputJsonConverterFactory>());
|
||||
options.Converters.Add(CreateInstance<ExpressionJsonConverterFactory>());
|
||||
options.Converters.Add(CreateInstance<FuncExpressionValueConverter>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public class JsonPayloadSerializer : IPayloadSerializer
|
|||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Serialize(object payload)
|
||||
{
|
||||
|
|
@ -82,8 +82,10 @@ public class JsonPayloadSerializer : IPayloadSerializer
|
|||
options.Converters.Add(GetService<PolymorphicObjectConverterFactory>());
|
||||
options.Converters.Add(GetService<TypeJsonConverter>());
|
||||
options.Converters.Add(GetService<VariableConverterFactory>());
|
||||
options.Converters.Add(new FuncExpressionValueConverter());
|
||||
return options;
|
||||
}
|
||||
|
||||
private T GetService<T>() where T : notnull => ActivatorUtilities.GetServiceOrCreateInstance<T>(_serviceProvider);
|
||||
}
|
||||
|
||||
private T GetService<T>()
|
||||
where T : notnull => ActivatorUtilities.GetServiceOrCreateInstance<T>(_serviceProvider);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ public class JsonWorkflowStateSerializer : ConfigurableSerializer, IWorkflowStat
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonWorkflowStateSerializer"/> class.
|
||||
/// </summary>
|
||||
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() };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,8 @@ namespace Elsa.Workflows.Serialization.Serializers;
|
|||
public class SafeSerializer : ConfigurableSerializer, ISafeSerializer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SafeSerializer(IServiceProvider serviceProvider) : base(serviceProvider)
|
||||
{
|
||||
}
|
||||
public SafeSerializer(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
[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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue