Fix Serialization of generic types including collections (#6148)
#5682 broke serialization in two ways: - When a generic type was being serialized, such as List`1, the ` was being escaped by JsonSerializer.Serialize to "\u0060". The PolymorphicObjectConverter would then .Trim('"') without unescaping the `, causing "\\u0060" to be written into the final json. - When the type is generic with one type parameter, e.g. List<T>, and also IEnumerable<T>, as List<T> is, then it would append "[]" at the end of the type name, resulting in: "System.Collections.Generic.List\\u00601[[Elsa.Workflows.IntegrationTests.Serialization.JsonSerialization.WorkflowAction, Elsa.Workflows.IntegrationTests]], System.Private.CoreLib[]" which is very invalid. It may only do that, if the typename was simplified because the elementType is in the WellKnownTypeRegistry. And even then, this is questionable, as you'd be deserializing an string[] where you've serialized a List<string> for example.
This commit is contained in:
parent
8502165dcc
commit
0a349451cd
|
|
@ -271,13 +271,10 @@ public class PolymorphicObjectConverter(IWellKnownTypeRegistry wellKnownTypeRegi
|
|||
{
|
||||
if (shouldWriteTypeField)
|
||||
{
|
||||
var typeOptions = newOptions.Clone();
|
||||
typeOptions.Converters.RemoveWhere(c => c.GetType() != typeof(TypeJsonConverter));
|
||||
|
||||
if (typeOptions.Converters.Any())
|
||||
if (newOptions.Converters.OfType<TypeJsonConverter>().FirstOrDefault() is { } typeJsonConverter)
|
||||
{
|
||||
var typeValue = JsonSerializer.Serialize(type, typeOptions).Trim('"');
|
||||
writer.WriteString(TypePropertyName, typeValue);
|
||||
writer.WritePropertyName(TypePropertyName);
|
||||
typeJsonConverter.Write(writer, type, newOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -51,15 +51,14 @@ public class TypeJsonConverter : JsonConverter<Type>
|
|||
var elementType = value.GenericTypeArguments.First();
|
||||
var typedEnumerable = typeof(IEnumerable<>).MakeGenericType(elementType);
|
||||
|
||||
if (typedEnumerable.IsAssignableFrom(value))
|
||||
if (typedEnumerable.IsAssignableFrom(value) && _wellKnownTypeRegistry.TryGetAlias(elementType, out var elementTypeAlias))
|
||||
{
|
||||
var elementTypeAlias = _wellKnownTypeRegistry.TryGetAlias(elementType, out var elementAlias) ? elementAlias : value.GetSimpleAssemblyQualifiedName();
|
||||
JsonSerializer.Serialize(writer, $"{elementTypeAlias}[]", options);
|
||||
writer.WriteStringValue($"{elementTypeAlias}[]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var typeAlias = _wellKnownTypeRegistry.TryGetAlias(value, out var @alias) ? alias : value.GetSimpleAssemblyQualifiedName();
|
||||
JsonSerializer.Serialize(writer, typeAlias, options);
|
||||
writer.WriteStringValue(typeAlias);
|
||||
}
|
||||
}
|
||||
|
|
@ -69,6 +69,26 @@ public class SerializationTests(ITestOutputHelper testOutputHelper)
|
|||
CompareJsonsObjects(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundtripComplexEnumerableObject()
|
||||
{
|
||||
var dict = new Dictionary<string, object>
|
||||
{
|
||||
{ "Content", new List<TestObject>()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Data = "Hello World"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
var jsonSerialized = SerializeUsingPayloadSerializer(dict);
|
||||
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonSerialized);
|
||||
var result = transformationModel["Content"];
|
||||
Assert.Equal(typeof(List<TestObject>), result.GetType());
|
||||
}
|
||||
|
||||
private string SerializeUsingPayloadSerializer(object obj)
|
||||
{
|
||||
var payloadSerializer = _services.GetRequiredService<IPayloadSerializer>();
|
||||
|
|
@ -93,12 +113,12 @@ public class SerializationTests(ITestOutputHelper testOutputHelper)
|
|||
{
|
||||
var isArray = type == typeof(JsonArray) || type == typeof(JArray);
|
||||
|
||||
var jsonContent = isArray ? "[{\"path\":\"folder1\",\"command\":\"add\"}]": "{\"file1\":{\"script\":[{\"path\":\"folder1\",\"command\":\"add\"}]} }";
|
||||
var jsonContent = isArray ? "[{\"path\":\"folder1\",\"command\":\"add\"}]" : "{\"file1\":{\"script\":[{\"path\":\"folder1\",\"command\":\"add\"}]} }";
|
||||
|
||||
var dict = new Dictionary<string, object>
|
||||
{
|
||||
{ "StatusCode", "Created" },
|
||||
{ "Content",isArray ?
|
||||
{ "Content",isArray ?
|
||||
(type == typeof(JArray)? JArray.Parse(jsonContent):JsonArray.Parse(jsonContent)):
|
||||
(type == typeof(JObject)? JObject.Parse(jsonContent):JsonObject.Parse(jsonContent))
|
||||
}
|
||||
|
|
@ -108,7 +128,8 @@ public class SerializationTests(ITestOutputHelper testOutputHelper)
|
|||
|
||||
private string GetExpected(Type type)
|
||||
{
|
||||
if (type == typeof(JsonArray) || type == typeof(JArray)) {
|
||||
if (type == typeof(JsonArray) || type == typeof(JArray))
|
||||
{
|
||||
return "[{\"path\":\"folder1\",\"command\":\"add\"}]";
|
||||
}
|
||||
else
|
||||
|
|
@ -118,4 +139,9 @@ public class SerializationTests(ITestOutputHelper testOutputHelper)
|
|||
}
|
||||
|
||||
private static string? NormalizeNewlines(string? input) => input?.Replace("\r\n", "\n").Replace("\\r\\n", "\\n");
|
||||
}
|
||||
|
||||
public class TestObject
|
||||
{
|
||||
public string? Data { get; set; }
|
||||
}
|
||||
Loading…
Reference in a new issue