From 3de859a602ea8a4f7b56b8656aaadfb329c572ee Mon Sep 17 00:00:00 2001 From: Corniel Nobel Date: Thu, 21 Nov 2024 10:45:21 +0100 Subject: [PATCH] Extend attribute info. --- .../OpenApi/JSON_serialization_specs.cs | 67 +++++++++++++++++++ .../Syntax/Attribute_decoration_specs.cs | 9 +-- src/Qowaiv.CodeGeneration/Nill.cs | 6 ++ .../Syntax/AttributeInfo.Constants.cs | 33 ++++++--- .../Syntax/AttributeInfo.cs | 18 +++++ src/Qowaiv.CodeGeneration/Syntax/Literal.cs | 1 + 6 files changed, 119 insertions(+), 15 deletions(-) create mode 100644 specs/Qowaiv.CodeGeneration.Specs/OpenApi/JSON_serialization_specs.cs create mode 100644 src/Qowaiv.CodeGeneration/Nill.cs diff --git a/specs/Qowaiv.CodeGeneration.Specs/OpenApi/JSON_serialization_specs.cs b/specs/Qowaiv.CodeGeneration.Specs/OpenApi/JSON_serialization_specs.cs new file mode 100644 index 0000000..8da6417 --- /dev/null +++ b/specs/Qowaiv.CodeGeneration.Specs/OpenApi/JSON_serialization_specs.cs @@ -0,0 +1,67 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Open_API.JSON_serialization_specs; + +public class Serializes +{ + [Test] + public void Model_with_derived_types() + { + var json = @"{""Items"":[{""Value1"":""1.34534"",""Type"":""Derived1""},{""Value2"":""2.85464"",""Type"":""Derived2""}]}"; + + var model = JsonSerializer.Deserialize(json); + + model.Should().BeEquivalentTo(new MyContainer + { + Items = + [ + new Derived1{ Type = "Derived1", Value1 = "1.34534" }, + new Derived2{ Type = "Derived2", Value2 = "2.85464" }, + ] + }); + } +} + +public class Deserializes +{ + [Test] + public void Model_with_derived_types() + { + var model = new MyContainer + { + Items = + [ + new Derived1{ Type = "Derived1", Value1 = "1.34534" }, + new Derived2{ Type = "Derived2", Value2 = "2.85464" }, + ] + }; + + var json = JsonSerializer.Serialize(model); + + + Console.WriteLine(json); + } +} + + +internal sealed record MyContainer +{ + public MyBase[] Items { get; init; } = []; +} + +[JsonDerivedType(typeof(Derived1))] +[JsonDerivedType(typeof(Derived2))] +internal record MyBase +{ + public string? Type { get; init; } +} +internal sealed record Derived1 : MyBase +{ + public string? Value1 { get; init; } +} + +internal sealed record Derived2 : MyBase +{ + public string? Value2 { get; init; } +} diff --git a/specs/Qowaiv.CodeGeneration.Specs/Syntax/Attribute_decoration_specs.cs b/specs/Qowaiv.CodeGeneration.Specs/Syntax/Attribute_decoration_specs.cs index cb4e0de..fa4943a 100644 --- a/specs/Qowaiv.CodeGeneration.Specs/Syntax/Attribute_decoration_specs.cs +++ b/specs/Qowaiv.CodeGeneration.Specs/Syntax/Attribute_decoration_specs.cs @@ -17,8 +17,7 @@ public void ctor_arguments() [Test] public void parameter_sets() - => new AttributeInfo(typeof(TestAttribute), - null, KeyValuePair.Create("Author", (object?)"Qowaiv")) + => new AttributeInfo(typeof(TestAttribute), null, new { Author = "Qowaiv" }) .Should().HaveContent("[NUnit.Framework.Test(Author = \"Qowaiv\")]\r\n"); [Test] @@ -32,11 +31,9 @@ public class Equaly [Test] public void by_value() { - var attr = new AttributeInfo(typeof(TestAttribute), - null, KeyValuePair.Create("Author", (object?)"Qowaiv")); + var attr = new AttributeInfo(typeof(TestAttribute), null, new { Author = "Qowaiv" }); - var ottr = new AttributeInfo(typeof(TestAttribute), - null, KeyValuePair.Create("Author", (object?)"Qowaiv")); + var ottr = new AttributeInfo(typeof(TestAttribute), null, new { Author = "Qowaiv" }); attr.Equals(ottr).Should().BeTrue(); } diff --git a/src/Qowaiv.CodeGeneration/Nill.cs b/src/Qowaiv.CodeGeneration/Nill.cs new file mode 100644 index 0000000..bae721c --- /dev/null +++ b/src/Qowaiv.CodeGeneration/Nill.cs @@ -0,0 +1,6 @@ +namespace Qowaiv.CodeGeneration; + +public sealed class Nill +{ + public static readonly Nill Value = new(); +} diff --git a/src/Qowaiv.CodeGeneration/Syntax/AttributeInfo.Constants.cs b/src/Qowaiv.CodeGeneration/Syntax/AttributeInfo.Constants.cs index 0435555..d4aea3f 100644 --- a/src/Qowaiv.CodeGeneration/Syntax/AttributeInfo.Constants.cs +++ b/src/Qowaiv.CodeGeneration/Syntax/AttributeInfo.Constants.cs @@ -5,6 +5,9 @@ public partial class AttributeInfo /// . public static readonly AttributeInfo Newtonsoft_Json_JsonConstructor = new(typeof(Newtonsoft.Json.JsonConstructorAttribute)); + /// . + public static readonly AttributeInfo Qowaiv_Diagnostics_Contracts_Inheritable = new(typeof(Qowaiv.Diagnostics.Contracts.InheritableAttribute)); + /// . public static readonly AttributeInfo Qowaiv_Validation_DataAnnotations_Any = new(typeof(Qowaiv.Validation.DataAnnotations.AnyAttribute)); @@ -68,10 +71,24 @@ public static AttributeInfo Qowaiv_Validation_DataAnnotations_AllowedValues(para public static AttributeInfo Qowaiv_Validation_DataAnnotations_MultipleOf(double factor) => new(typeof(Qowaiv.Validation.DataAnnotations.MultipleOfAttribute), [factor]); + /// . + [Pure] + public static AttributeInfo System_Text_Json_SerializationJsonPolymorphic( + string name, + System.Text.Json.Serialization.JsonUnknownDerivedTypeHandling? handling = default, + bool? ignoreUnrecognized = false) => new( + typeof(System.Text.Json.Serialization.JsonPolymorphicAttribute), + [name], + new + { + UnknownDerivedTypeHandling = handling, + IgnoreUnrecognizedTypeDiscriminators = ignoreUnrecognized, + }); + /// [Pure] - public static AttributeInfo System_Text_Json_Serialization_JsonConverter() where T: System.Text.Json.Serialization.JsonConverter - => System_Text_Json_Serialization_JsonConverter(typeof(T)); + public static AttributeInfo System_Text_Json_Serialization_JsonConverter() where TConverter : System.Text.Json.Serialization.JsonConverter + => System_Text_Json_Serialization_JsonConverter(typeof(TConverter)); /// . [Pure] @@ -80,13 +97,14 @@ public static AttributeInfo System_Text_Json_Serialization_JsonConverter(Type ty /// . [Pure] - public static AttributeInfo System_Text_Json_Serialization_JsonDerivedType(Type type) - => new(typeof(System.Text.Json.Serialization.JsonDerivedTypeAttribute), [type]); + public static AttributeInfo System_Text_Json_Serialization_JsonDerivedType(Type type, string? discriminator = null) => new( + typeof(System.Text.Json.Serialization.JsonDerivedTypeAttribute), + discriminator is { } ? [type, discriminator] : [type]); /// . [Pure] public static AttributeInfo System_Text_Json_Serialization_JsonIgnore(System.Text.Json.Serialization.JsonIgnoreCondition condition) - => new(typeof(System.Text.Json.Serialization.JsonIgnoreAttribute), null, Kvp(nameof(System.Text.Json.Serialization.JsonIgnoreAttribute.Condition), condition)); + => new(typeof(System.Text.Json.Serialization.JsonIgnoreAttribute), null, new { Condition = condition }); /// . [Pure] @@ -96,8 +114,5 @@ public static AttributeInfo System_Text_Json_Serialization_JsonPropertyName(stri /// . [Pure] public static AttributeInfo System_Runtime_Serialization_EnumMember(string? value) - => new(typeof(System.Runtime.Serialization.EnumMemberAttribute), null, Kvp("Value", (object?)value)); - - [Pure] - private static KeyValuePair Kvp(string key, object? value) => new(key, value); + => new(typeof(System.Runtime.Serialization.EnumMemberAttribute), null, new { Value = (object?)value ?? Nill.Value }); } diff --git a/src/Qowaiv.CodeGeneration/Syntax/AttributeInfo.cs b/src/Qowaiv.CodeGeneration/Syntax/AttributeInfo.cs index e9c8999..465ce33 100644 --- a/src/Qowaiv.CodeGeneration/Syntax/AttributeInfo.cs +++ b/src/Qowaiv.CodeGeneration/Syntax/AttributeInfo.cs @@ -4,6 +4,7 @@ namespace Qowaiv.CodeGeneration.Syntax; public sealed partial class AttributeInfo : Code, IEquatable { /// Initializes a new instance of the class. + [Obsolete("Use new AttributeInfo(Type, object[]?, dynamic) instead.")] public AttributeInfo(Type attribute, object[]? ctorArguments = null, params KeyValuePair[] propertyValues) { AttributeType = Guard.NotNull(attribute); @@ -11,6 +12,14 @@ public AttributeInfo(Type attribute, object[]? ctorArguments = null, params KeyV PropertyValues = propertyValues ?? []; } + /// Initializes a new instance of the class. + public AttributeInfo(Type attribute, object[]? ctorArguments = null, dynamic? propertyValues = null) + { + AttributeType = Guard.NotNull(attribute); + CtorArguments = ctorArguments ?? []; + PropertyValues = propertyValues is null ? [] : FromDynamic(propertyValues); + } + /// The type of the attribute. public Type AttributeType { get; } @@ -120,4 +129,13 @@ public override int GetHashCode() /// Returns false if both have the same values. public static bool operator !=(AttributeInfo? l, AttributeInfo? r) => !(l == r); + + /// Creates a dictionary from a a dynamic object. + [Pure] + private static IReadOnlyCollection> FromDynamic(object obj) + => obj.GetType() + .GetProperties() + .Select(p => KeyValuePair.Create(p.Name, p.GetValue(obj))) + .Where(kvp => kvp.Value is { }) + .ToDictionary(); } diff --git a/src/Qowaiv.CodeGeneration/Syntax/Literal.cs b/src/Qowaiv.CodeGeneration/Syntax/Literal.cs index ba92585..e70ae37 100644 --- a/src/Qowaiv.CodeGeneration/Syntax/Literal.cs +++ b/src/Qowaiv.CodeGeneration/Syntax/Literal.cs @@ -15,6 +15,7 @@ public void WriteTo(CSharpWriter writer) Guard.NotNull(writer); _ = Value switch { + Nill or null /*.........*/ => writer.Write("null"), Type type /*....*/ => writer.Write("typeof(").Write(type).Write(')'), bool boolean /*.*/ => writer.Write(boolean ? "true" : "false"),