From 4056434e45c69eb24ed035220f8ca8c94d5fd136 Mon Sep 17 00:00:00 2001 From: Nokecy Date: Wed, 18 Oct 2023 05:14:40 +0800 Subject: [PATCH] fix: fix hashSet deserialize error (#4545) * update hangfire version to 1.8.5 * fix: fix hashSet deserialize error * fix: replace HashSet to ISet * Update polymorphic converter and tests --------- Co-authored-by: Sipke Schoorstra --- .../Elsa.Workflows.Core/Activities/Fork.cs | 1 - .../Converters/PolymorphicObjectConverter.cs | 21 ++++++++++---- .../Serialization/Polymorphism/Models.cs | 6 ++-- .../Serialization/Polymorphism/Tests.cs | 29 +++++-------------- .../Serialization/Polymorphism/data.json | 2 +- 5 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/modules/Elsa.Workflows.Core/Activities/Fork.cs b/src/modules/Elsa.Workflows.Core/Activities/Fork.cs index 267eb0a69..ab1491a7f 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Fork.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Fork.cs @@ -33,7 +33,6 @@ public class Fork : Activity /// /// The branches to schedule. /// - [Port] public ICollection Branches { get; set; } = new List(); /// diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Converters/PolymorphicObjectConverter.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/PolymorphicObjectConverter.cs index 0e517c8c4..352585a67 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Converters/PolymorphicObjectConverter.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/PolymorphicObjectConverter.cs @@ -117,12 +117,15 @@ public class PolymorphicObjectConverter : JsonConverter var values = model.TryGetProperty(ItemsPropertyName, out var itemsProp) ? itemsProp.EnumerateArray().ToList() : model.GetProperty(ValuesPropertyName).EnumerateArray().ToList(); var id = model.TryGetProperty(IdPropertyName, out var idProp) ? idProp.GetString() : default; - var collection = targetType.IsArray ? Array.CreateInstance(elementType, values.Count) : (IList)Activator.CreateInstance(targetType)!; + var collection = targetType.IsArray ? Array.CreateInstance(elementType, values.Count) : Activator.CreateInstance(targetType)!; var index = 0; if (id != null) referenceResolver?.AddReference(id, collection); + var isHashSet = targetType.GenericTypeArguments.Length == 1 && typeof(ISet<>).MakeGenericType(targetType.GenericTypeArguments[0]).IsAssignableFrom(targetType); + var addSetMethod = targetType.GetMethod("Add", new[] { elementType })!; + foreach (var element in values) { var deserializedElement = JsonSerializer.Deserialize(JsonSerializer.Serialize(element), elementType, newOptions)!; @@ -130,9 +133,13 @@ public class PolymorphicObjectConverter : JsonConverter { array.SetValue(deserializedElement, index++); } - else + else if (isHashSet) { - collection.Add(deserializedElement); + addSetMethod.Invoke(collection, new[] { deserializedElement }); + } + else if (collection is IList list) + { + list.Add(deserializedElement); } } @@ -163,7 +170,7 @@ public class PolymorphicObjectConverter : JsonConverter // Special case for Newtonsoft.Json and System.Text.Json types. // Newtonsoft.Json types are not supported by the System.Text.Json serializer and should be written as a string instead. - // We include metadata about the type so that we can deserialize it later. + // We include metadata about the type so that we can deserialize it later. if (type == typeof(JObject) || type == typeof(JArray) || type == typeof(JsonObject) || type == typeof(JsonArray)) { writer.WriteStartObject(); @@ -200,7 +207,7 @@ public class PolymorphicObjectConverter : JsonConverter value = sanitized; } - + var jsonElement = JsonDocument.Parse(JsonSerializer.Serialize(value, type, newOptions)).RootElement; // If the value is a string, serialize it directly. @@ -265,6 +272,7 @@ public class PolymorphicObjectConverter : JsonConverter case JsonTokenType.StartArray: depth++; break; + case JsonTokenType.EndObject: case JsonTokenType.EndArray: depth--; @@ -309,6 +317,7 @@ public class PolymorphicObjectConverter : JsonConverter default: list.Add(Read(ref reader, typeof(object), options)); break; + case JsonTokenType.EndArray: return list; } @@ -328,6 +337,7 @@ public class PolymorphicObjectConverter : JsonConverter if (dict.Count == 1 && dict.TryGetValue(RefPropertyName, out var referencedObject)) return referencedObject; return dict; + case JsonTokenType.PropertyName: var key = reader.GetString()!; reader.Read(); @@ -350,6 +360,7 @@ public class PolymorphicObjectConverter : JsonConverter } break; + default: throw new JsonException(); } diff --git a/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/Models.cs b/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/Models.cs index 6e7a12efd..4550ab9ff 100644 --- a/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/Models.cs +++ b/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/Models.cs @@ -13,10 +13,10 @@ public record Model( ICollection? Items = default, object? Metadata = default, object? Payload = default, - IDictionary? Properties = default -) + object? HashSet = default, + IDictionary? Properties = default) { - public Model() : this(null, default, default, null, default, default, default) + public Model() : this(null, default, default, null, default, default, default, default) { } } diff --git a/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/Tests.cs b/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/Tests.cs index 08ae89e07..e6c83084c 100644 --- a/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/Tests.cs +++ b/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/Tests.cs @@ -58,33 +58,20 @@ public class Tests }, metadata, new Model("Payload"), + new HashSet + { + new() + { + Text = "I'm a model in a set!" + } + }, new Dictionary { ["Hello"] = new("Hello", 1, true), - ["World"] = new("World", 2) + ["World"] = new("World", 2), } ); } - - private Model CreateModel2() - { - // Create a model with various nested properties - var metadata = new ExpandoObject() as IDictionary; - - // Initialize metadata with test data. - metadata["Models"] = new List - { - new(Text: "Hello World") - }; - - return new Model( - Metadata: metadata - // Items : new List - // { - // new(Items: new List { new(Metadata: metadata) }), - // } - ); - } private JsonSerializerOptions GetSerializerOptions() { diff --git a/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/data.json b/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/data.json index 8013a6881..8c1e1e655 100644 --- a/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/data.json +++ b/test/integration/Elsa.IntegrationTests/Serialization/Polymorphism/data.json @@ -1 +1 @@ -{"$id":"1","text":"Hello World","number":123,"flag":true,"items":{"$id":"2","$values":[{"$id":"3","text":"Hello","number":1,"flag":true,"items":{"$id":"4","$values":[{"$id":"5","number":0,"flag":false,"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$id":"6","$values":[{"$id":"7","text":"Hello","number":1,"flag":true,"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$ref":"6"},"CustomDictionary":{"$id":"8","content-type":["application/json"],"_type":"Elsa.IntegrationTests.Serialization.Polymorphism.CustomDictionary, Elsa.IntegrationTests"}}},{"$id":"9","text":"World","number":2,"flag":false}],"_type":"System.Collections.Generic.List\u00601[[Elsa.IntegrationTests.Serialization.Polymorphism.Model, Elsa.IntegrationTests]], System.Private.CoreLib"},"CustomDictionary":{"$ref":"8"}}}]}},{"$id":"10","text":"World","number":2,"flag":false}]},"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$ref":"6"},"CustomDictionary":{"$ref":"8"}},"payload":{"$id":"11","text":"Payload","number":0,"flag":false,"_type":"Elsa.IntegrationTests.Serialization.Polymorphism.Model, Elsa.IntegrationTests"},"properties":{"$id":"12","Hello":{"$id":"13","text":"Hello","number":1,"flag":true},"World":{"$id":"14","text":"World","number":2,"flag":false}}} \ No newline at end of file +{"$id":"1","text":"Hello World","number":123,"flag":true,"items":{"$id":"2","$values":[{"$id":"3","text":"Hello","number":1,"flag":true,"items":{"$id":"4","$values":[{"$id":"5","number":0,"flag":false,"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$id":"6","$values":[{"$id":"7","text":"Hello","number":1,"flag":true,"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$ref":"6"},"CustomDictionary":{"$id":"8","content-type":["application/json"],"_type":"Elsa.IntegrationTests.Serialization.Polymorphism.CustomDictionary, Elsa.IntegrationTests"}}},{"$id":"9","text":"World","number":2,"flag":false}],"_type":"System.Collections.Generic.List\u00601[[Elsa.IntegrationTests.Serialization.Polymorphism.Model, Elsa.IntegrationTests]], System.Private.CoreLib"},"CustomDictionary":{"$ref":"8"}}}]}},{"$id":"10","text":"World","number":2,"flag":false}]},"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$ref":"6"},"CustomDictionary":{"$ref":"8"}},"payload":{"$id":"11","text":"Payload","number":0,"flag":false,"_type":"Elsa.IntegrationTests.Serialization.Polymorphism.Model, Elsa.IntegrationTests"},"hashSet":{"$id":"12","$values":[{"$id":"13","text":"I\u0027m a model in a set!","number":0,"flag":false}],"_type":"System.Collections.Generic.HashSet\u00601[[Elsa.IntegrationTests.Serialization.Polymorphism.Model, Elsa.IntegrationTests]], System.Private.CoreLib"},"properties":{"$id":"14","Hello":{"$id":"15","text":"Hello","number":1,"flag":true},"World":{"$id":"16","text":"World","number":2,"flag":false}}} \ No newline at end of file