Improve JSON array conversion in ObjectConverter

Refactored ObjectConverter to handle JSON array conversions more robustly, including support for arrays of complex types. Added a `Person` class for unit testing and updated tests to validate the new functionality. Included necessary project reference updates to ensure proper functionality.
This commit is contained in:
Sipke Schoorstra 2025-01-29 10:50:42 +01:00
parent 9437ab3a3d
commit 0560d11432
4 changed files with 48 additions and 7 deletions

View file

@ -15,6 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\..\common\Elsa.Features\Elsa.Features.csproj" />
<ProjectReference Include="..\Elsa.Common\Elsa.Common.csproj" />
</ItemGroup>
</Project>

View file

@ -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<ExpandoObject>(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<ExpandoObject>(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))

View file

@ -0,0 +1,9 @@
namespace Elsa.Workflows.Core.UnitTests.ObjectConversion
{
public class Person
{
public double? Age { get; set; }
public string? Name { get; set; }
}
}

View file

@ -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<Person[]>(options);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Length);
Assert.Equal("Alice", result[0].Name);
Assert.Equal("Bob", result[1].Name);
}
}