diff --git a/src/modules/Elsa.Expressions/Elsa.Expressions.csproj b/src/modules/Elsa.Expressions/Elsa.Expressions.csproj index d696bff63..66d1a6e5e 100644 --- a/src/modules/Elsa.Expressions/Elsa.Expressions.csproj +++ b/src/modules/Elsa.Expressions/Elsa.Expressions.csproj @@ -15,6 +15,7 @@ + diff --git a/src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs b/src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs index f59e9861c..899a84ddc 100644 --- a/src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs +++ b/src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs @@ -102,15 +102,29 @@ public static class ObjectConverter return jsonElement.Deserialize(targetType, serializerOptions); } - if (value is JsonNode jsonNode and not JsonArray) // If the value is a JsonNode, we can convert it to the target type. If it's a JsonArray, we let the enumerable conversion logic handle it. + if (value is JsonNode jsonNode) { - return underlyingTargetType switch + if (jsonNode is not JsonArray jsonArray) { - { } t when t == typeof(string) => jsonNode.ToString(), - { } t when t == typeof(ExpandoObject) && jsonNode.GetValueKind() == JsonValueKind.Object => JsonSerializer.Deserialize(jsonNode.ToJsonString()), - { } t when t != typeof(object) || converterOptions?.DeserializeJsonObjectToObject == true => jsonNode.Deserialize(targetType, serializerOptions), - _ => jsonNode - }; + return underlyingTargetType switch + { + { } t when t == typeof(string) => jsonNode.ToString(), + { } t when t == typeof(ExpandoObject) && jsonNode.GetValueKind() == JsonValueKind.Object => JsonSerializer.Deserialize(jsonNode.ToJsonString()), + { } t when t != typeof(object) || converterOptions?.DeserializeJsonObjectToObject == true => jsonNode.Deserialize(targetType, serializerOptions), + _ => jsonNode + }; + } + + // Convert to target type if target type is an array or a generic collection. + if (targetType.IsArray || targetType.IsCollectionType()) + { + // The element type of the source array is JsonObject. If the element type of the target array is Object then return the source array as an array of JsonObjects. + // Deserializing normally would return an array of JsonElement instead of JsonObject, but we want to keep JsonObject elements: + var targetElementType = targetType.IsArray ? targetType.GetElementType()! : targetType.GenericTypeArguments[0]; + + if (targetElementType != typeof(object)) + return jsonArray.Deserialize(targetType, serializerOptions); + } } if (underlyingSourceType == typeof(string) && !underlyingTargetType.IsPrimitive && underlyingTargetType != typeof(object)) diff --git a/test/unit/Elsa.Workflows.Core.UnitTests/ObjectConversion/Person.cs b/test/unit/Elsa.Workflows.Core.UnitTests/ObjectConversion/Person.cs new file mode 100644 index 000000000..b5583db8b --- /dev/null +++ b/test/unit/Elsa.Workflows.Core.UnitTests/ObjectConversion/Person.cs @@ -0,0 +1,9 @@ +namespace Elsa.Workflows.Core.UnitTests.ObjectConversion +{ + public class Person + { + public double? Age { get; set; } + + public string? Name { get; set; } + } +} diff --git a/test/unit/Elsa.Workflows.Core.UnitTests/ObjectConversion/Tests.cs b/test/unit/Elsa.Workflows.Core.UnitTests/ObjectConversion/Tests.cs index 6525f5d0e..c245cb5cc 100644 --- a/test/unit/Elsa.Workflows.Core.UnitTests/ObjectConversion/Tests.cs +++ b/test/unit/Elsa.Workflows.Core.UnitTests/ObjectConversion/Tests.cs @@ -283,4 +283,21 @@ public class Tests Assert.Equal("Bob", secondElement["name"]?.ToString()); Assert.Equal("25", secondElement["age"]?.ToString()); } + + [Fact] + public void ConvertFrom_JsonArrayToArrayOfComplextType_ReturnsArrayOfComplexType() + { + // Arrange + var jsonArrayString = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]"; + var options = new ObjectConverterOptions(); + + // Act + var result = jsonArrayString.ConvertTo(options); + + // Assert + Assert.NotNull(result); + Assert.Equal(2, result.Length); + Assert.Equal("Alice", result[0].Name); + Assert.Equal("Bob", result[1].Name); + } } \ No newline at end of file