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:
Robin Sue 2024-11-26 09:19:16 +01:00 committed by GitHub
parent 8502165dcc
commit 0a349451cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 35 additions and 13 deletions

View file

@ -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
{

View file

@ -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);
}
}

View file

@ -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; }
}