From f905855720113c876593adfd34943c02bc0a0d24 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Fri, 14 Dec 2018 18:19:08 +0100 Subject: [PATCH] Serialization fixes. --- .../Controllers/MyJsonInheritanceConverter.cs | 85 ++++++++++++++----- .../Controllers/Rules/Models/CreateRuleDto.cs | 2 +- ...onSerializer.cs => RuleActionConverter.cs} | 10 ++- .../Rules/Models/RuleActionProcessor.cs | 9 +- .../Api/Controllers/Rules/Models/RuleDto.cs | 2 +- .../Rules/Models/RuleTriggerDto.cs | 2 +- .../Controllers/Rules/Models/UpdateRuleDto.cs | 2 +- .../Schemas/Models/FieldPropertiesDto.cs | 2 +- 8 files changed, 79 insertions(+), 35 deletions(-) rename src/Squidex/Areas/Api/Controllers/Rules/Models/{RuleActionSerializer.cs => RuleActionConverter.cs} (60%) diff --git a/src/Squidex/Areas/Api/Controllers/MyJsonInheritanceConverter.cs b/src/Squidex/Areas/Api/Controllers/MyJsonInheritanceConverter.cs index 27748dd35..36a394b5b 100644 --- a/src/Squidex/Areas/Api/Controllers/MyJsonInheritanceConverter.cs +++ b/src/Squidex/Areas/Api/Controllers/MyJsonInheritanceConverter.cs @@ -8,42 +8,85 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using Newtonsoft.Json.Linq; using NJsonSchema.Converters; +using Squidex.Infrastructure; + +#pragma warning disable RECS0108 // Warns about static fields in generic types namespace Squidex.Areas.Api.Controllers { - public class MyJsonInheritanceConverter : JsonInheritanceConverter + public class MyJsonInheritanceConverter : JsonInheritanceConverter { - private readonly IDictionary mapping; - private readonly Type baseType; + private static readonly Dictionary DefaultMapping = new Dictionary(); + private readonly IReadOnlyDictionary maping; - public MyJsonInheritanceConverter(string discriminator, Type baseType) - : base(baseType, discriminator) + static MyJsonInheritanceConverter() { - this.baseType = baseType; - } + var baseName = typeof(T).Name; - public MyJsonInheritanceConverter(string discriminator, Type baseType, IDictionary mapping) - : this(discriminator, baseType) - { - this.mapping = mapping; - } + void AddType(Type type) + { + var discriminator = type.Name; - public override string GetDiscriminatorValue(Type type) - { - var result = type.Name; + if (discriminator.EndsWith(baseName, StringComparison.CurrentCulture)) + { + discriminator = discriminator.Substring(0, discriminator.Length - baseName.Length); + } - if (baseType != null) - { - var baseName = baseType.Name; + DefaultMapping[discriminator] = type; + } - if (result.EndsWith(baseName, StringComparison.CurrentCulture)) + foreach (var attribute in typeof(T).GetCustomAttributes()) + { + if (attribute.Type != null) + { + if (!attribute.Type.IsAbstract) + { + AddType(attribute.Type); + } + } + else if (!string.IsNullOrWhiteSpace(attribute.MethodName)) { - return result.Substring(0, result.Length - baseName.Length); + var method = typeof(T).GetMethod(attribute.MethodName); + + if (method != null && method.IsStatic) + { + var types = (IEnumerable)method.Invoke(null, new object[0]); + + foreach (var type in types) + { + if (!type.IsAbstract) + { + AddType(type); + } + } + } } } + } + + public MyJsonInheritanceConverter(string discriminator) + : this(discriminator, DefaultMapping) + { + } + + public MyJsonInheritanceConverter(string discriminator, IReadOnlyDictionary mapping) + : base(typeof(T), discriminator) + { + maping = mapping; + } - return mapping?.FirstOrDefault(x => x.Value == type).Key ?? result; + protected override Type GetDiscriminatorType(JObject jObject, Type objectType, string discriminatorValue) + { + return maping.GetOrDefault(discriminatorValue) ?? throw new InvalidOperationException($"Could not find subtype of '{objectType.Name}' with discriminator '{discriminatorValue}'."); + } + + public override string GetDiscriminatorValue(Type type) + { + return maping.FirstOrDefault(x => x.Value == type).Key ?? type.Name; } } } \ No newline at end of file diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/CreateRuleDto.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/CreateRuleDto.cs index e199ed4e2..235637e99 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/CreateRuleDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/CreateRuleDto.cs @@ -24,7 +24,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models /// The action properties. /// [Required] - [JsonConverter(typeof(RuleActionSerializer))] + [JsonConverter(typeof(RuleActionConverter))] public RuleAction Action { get; set; } public CreateRule ToCommand() diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionSerializer.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionConverter.cs similarity index 60% rename from src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionSerializer.cs rename to src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionConverter.cs index 8eb1d91f1..4efcfb7ad 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionSerializer.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionConverter.cs @@ -5,16 +5,20 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; +using System.Collections.Generic; using System.Linq; using Squidex.Domain.Apps.Core.Rules; using Squidex.Extensions.Actions; namespace Squidex.Areas.Api.Controllers.Rules.Models { - public sealed class RuleActionSerializer : MyJsonInheritanceConverter + public sealed class RuleActionConverter : MyJsonInheritanceConverter { - public RuleActionSerializer() - : base("actionType", typeof(RuleAction), RuleElementRegistry.Actions.ToDictionary(x => x.Key, x => x.Value.Type)) + private static readonly Dictionary Mapping = RuleElementRegistry.Actions.ToDictionary(x => x.Key, x => x.Value.Type); + + public RuleActionConverter() + : base("actionType", Mapping) { } } diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs index 339edef26..a0d23e12b 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs @@ -26,17 +26,14 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models if (schema != null) { - var discriminator = new OpenApiDiscriminator + schema.DiscriminatorObject = new OpenApiDiscriminator { - JsonInheritanceConverter = new MyJsonInheritanceConverter("actionType", typeof(RuleAction)), - PropertyName = "actionType" + JsonInheritanceConverter = new RuleActionConverter(), PropertyName = "actionType" }; - schema.DiscriminatorObject = discriminator; schema.Properties["actionType"] = new JsonProperty { - Type = JsonObjectType.String, - IsRequired = true + Type = JsonObjectType.String, IsRequired = true }; foreach (var derived in RuleElementRegistry.Actions) diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs index 7e3f7c85a..2ca337a42 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs @@ -67,7 +67,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models /// The action properties. /// [Required] - [JsonConverter(typeof(RuleActionSerializer))] + [JsonConverter(typeof(RuleActionConverter))] public RuleAction Action { get; set; } public static RuleDto FromRule(IRuleEntity rule) diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleTriggerDto.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleTriggerDto.cs index da5d5399d..7671db829 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleTriggerDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleTriggerDto.cs @@ -13,7 +13,7 @@ using Squidex.Domain.Apps.Core.Rules; namespace Squidex.Areas.Api.Controllers.Rules.Models { - [JsonConverter(typeof(MyJsonInheritanceConverter), "triggerType", typeof(RuleTriggerDto))] + [JsonConverter(typeof(MyJsonInheritanceConverter), "triggerType")] [KnownType(nameof(Subtypes))] public abstract class RuleTriggerDto { diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/UpdateRuleDto.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/UpdateRuleDto.cs index 831a0c5f6..adfe9099b 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/UpdateRuleDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/UpdateRuleDto.cs @@ -22,7 +22,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models /// /// The action properties. /// - [JsonConverter(typeof(RuleActionSerializer))] + [JsonConverter(typeof(RuleActionConverter))] public RuleAction Action { get; set; } public UpdateRule ToCommand(Guid id) diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs index e08a73b60..afb21c6b9 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs @@ -14,7 +14,7 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Areas.Api.Controllers.Schemas.Models { - [JsonConverter(typeof(MyJsonInheritanceConverter), "fieldType", typeof(FieldPropertiesDto))] + [JsonConverter(typeof(MyJsonInheritanceConverter), "fieldType")] [KnownType(nameof(Subtypes))] public abstract class FieldPropertiesDto {