diff --git a/ExtendableEnums.Microsoft.AspNetCore.UnitTests/ModelBindingTests.cs b/ExtendableEnums.Microsoft.AspNetCore.UnitTests/ModelBindingTests.cs index 089266a..0605dfd 100644 --- a/ExtendableEnums.Microsoft.AspNetCore.UnitTests/ModelBindingTests.cs +++ b/ExtendableEnums.Microsoft.AspNetCore.UnitTests/ModelBindingTests.cs @@ -13,6 +13,19 @@ public class ModelBindingTests : IDisposable private bool hasDisposed; + private JsonSerializerSettings options = new(); + + [TestInitialize] + public void TestInitialize() + { + var options = new JsonSerializerSettings + { + Formatting = Formatting.Indented + }; + options.Converters.AddExtendableEnums(); + this.options = options; + } + ~ModelBindingTests() { // Do not change this code. Put cleanup code in Dispose(bool disposing). @@ -37,7 +50,7 @@ public async Task BindTheExtendableEnumCorrectlyGivenIntBasedValue() Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(true); - var book = JsonConvert.DeserializeObject(responseContent); + var book = JsonConvert.DeserializeObject(responseContent, this.options); Assert.AreEqual(SampleStatus.Deleted, book?.Status); } @@ -59,7 +72,7 @@ public async Task BindTheExtendableEnumCorrectlyGivenNonExistentIntBasedValue() Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(true); - var book = JsonConvert.DeserializeObject(responseContent); + var book = JsonConvert.DeserializeObject(responseContent, this.options); Assert.IsNull(book?.Status); } @@ -82,7 +95,7 @@ public async Task BindTheExtendableEnumCorrectlyGivenNullIntBasedValue() Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(true); - var book = JsonConvert.DeserializeObject(responseContent); + var book = JsonConvert.DeserializeObject(responseContent, this.options); Assert.IsNull(book?.Status); } @@ -105,7 +118,7 @@ public async Task BindTheExtendableEnumCorrectlyGivenStringBasedValue() Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(true); - var book = JsonConvert.DeserializeObject(responseContent); + var book = JsonConvert.DeserializeObject(responseContent, this.options); Assert.AreEqual(SampleStatusByString.Deleted, book?.Status); } diff --git a/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnumBinder.cs b/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnumBinder.cs index e9c3f3b..93c8fb6 100644 --- a/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnumBinder.cs +++ b/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnumBinder.cs @@ -1,7 +1,5 @@ -using System; -using System.Threading.Tasks; +using System.Text.Json; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Newtonsoft.Json; namespace ExtendableEnums.Microsoft.AspNetCore; @@ -10,6 +8,13 @@ namespace ExtendableEnums.Microsoft.AspNetCore; /// public class ExtendableEnumBinder : IModelBinder { + private JsonSerializerOptions jsonSerializerOptions; + + public ExtendableEnumBinder(JsonSerializerOptions jsonSerializerOptions) + { + this.jsonSerializerOptions = jsonSerializerOptions; + } + /// /// Attempts to bind a model. /// @@ -37,7 +42,8 @@ public Task BindModelAsync(ModelBindingContext bindingContext) return Task.CompletedTask; } - var result = JsonConvert.DeserializeObject($"'{valueProviderResult.FirstValue}'", bindingContext.ModelType); + var json = $"{{\"value\":\"{valueProviderResult.FirstValue}\"}}"; + var result = JsonSerializer.Deserialize(json, returnType: bindingContext.ModelType, options: this.jsonSerializerOptions); bindingContext.Result = ModelBindingResult.Success(result); diff --git a/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnumModelBinderProvider.cs b/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnumModelBinderProvider.cs index 91303a6..5d0abb3 100644 --- a/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnumModelBinderProvider.cs +++ b/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnumModelBinderProvider.cs @@ -1,5 +1,7 @@ -using System; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; namespace ExtendableEnums.Microsoft.AspNetCore; @@ -22,7 +24,8 @@ public class ExtendableEnumModelBinderProvider : IModelBinderProvider if (context.Metadata.ModelType.IsExtendableEnum()) { - return new ExtendableEnumBinder(); + var options = context.Services.GetRequiredService>().Value; + return new ExtendableEnumBinder(options.JsonSerializerOptions); } return null; diff --git a/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnums.Microsoft.AspNetCore.csproj b/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnums.Microsoft.AspNetCore.csproj index adf7e81..c7fa06e 100644 --- a/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnums.Microsoft.AspNetCore.csproj +++ b/ExtendableEnums.Microsoft.AspNetCore/ExtendableEnums.Microsoft.AspNetCore.csproj @@ -45,5 +45,6 @@ + \ No newline at end of file diff --git a/ExtendableEnums/Serialization/Newtonsoft/ExtendableEnumDictionaryJsonConverter.cs b/ExtendableEnums.Serialization.Newtonsoft.Json/ExtendableEnumDictionaryJsonConverter.cs similarity index 97% rename from ExtendableEnums/Serialization/Newtonsoft/ExtendableEnumDictionaryJsonConverter.cs rename to ExtendableEnums.Serialization.Newtonsoft.Json/ExtendableEnumDictionaryJsonConverter.cs index 8ca011a..6d1731e 100644 --- a/ExtendableEnums/Serialization/Newtonsoft/ExtendableEnumDictionaryJsonConverter.cs +++ b/ExtendableEnums.Serialization.Newtonsoft.Json/ExtendableEnumDictionaryJsonConverter.cs @@ -1,7 +1,7 @@ using System.Collections; -using Newtonsoft.Json; +using ExtendableEnums; -namespace ExtendableEnums.Serialization.Newtonsoft; +namespace Newtonsoft.Json; /// /// Converts objects to and from JSON. diff --git a/ExtendableEnums/Serialization/Newtonsoft/ExtendableEnumJsonConverter.cs b/ExtendableEnums.Serialization.Newtonsoft.Json/ExtendableEnumJsonConverter.cs similarity index 79% rename from ExtendableEnums/Serialization/Newtonsoft/ExtendableEnumJsonConverter.cs rename to ExtendableEnums.Serialization.Newtonsoft.Json/ExtendableEnumJsonConverter.cs index a68557b..cf78484 100644 --- a/ExtendableEnums/Serialization/Newtonsoft/ExtendableEnumJsonConverter.cs +++ b/ExtendableEnums.Serialization.Newtonsoft.Json/ExtendableEnumJsonConverter.cs @@ -1,9 +1,9 @@ using System.Diagnostics; using System.Globalization; -using ExtendableEnums.Internals; -using Newtonsoft.Json; +using ExtendableEnums; +using Newtonsoft.Json.Internals; -namespace ExtendableEnums.Serialization.Newtonsoft; +namespace Newtonsoft.Json; /// /// Converts ExtendableEnum objects to and from JSON. @@ -17,7 +17,34 @@ public class ExtendableEnumJsonConverter : JsonConverter /// true if this instance can convert the specified object type; otherwise, false. public override bool CanConvert(Type objectType) { - return true; + if (objectType is null) + { + throw new ArgumentNullException(nameof(objectType)); + } + + return IsExtendableEnum(objectType); + } + + private static bool IsExtendableEnum(Type objectType) + { + if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(ExtendableEnumBase<,>)) + { + return true; + } + + // traverse type hierarchy + var baseType = objectType.BaseType; + while (baseType != null) + { + if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(ExtendableEnumBase<,>)) + { + return true; + } + + baseType = baseType.BaseType; + } + + return false; } /// @@ -45,7 +72,13 @@ public override bool CanConvert(Type objectType) throw new ArgumentNullException(nameof(serializer)); } - var valueType = objectType.GetExtendableEnumArgs()[1]; + var enumArgs = objectType.GetExtendableEnumArgs(); + if (enumArgs.Length < 2) + { + return null; + } + + var valueType = enumArgs[1]; var rawValue = reader.Value; if (rawValue is null) diff --git a/ExtendableEnums.Serialization.Newtonsoft.Json/ExtendableEnums.Serialization.Newtonsoft.Json.csproj b/ExtendableEnums.Serialization.Newtonsoft.Json/ExtendableEnums.Serialization.Newtonsoft.Json.csproj new file mode 100644 index 0000000..eb90f3b --- /dev/null +++ b/ExtendableEnums.Serialization.Newtonsoft.Json/ExtendableEnums.Serialization.Newtonsoft.Json.csproj @@ -0,0 +1,49 @@ + + + netstandard2.0 + 13.0 + enable + enable + 9.1 + 0 + 9.1 + https://github.com/kyleherzog/ExtendableEnums + true + Enum Enumeration Extendable Class + https://github.com/kyleherzog/ExtendableEnums + Kyle Herzog + A .NET Standard library that provides extendable class based enums. + Newtonsoft.Json + + + true + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + \ No newline at end of file diff --git a/ExtendableEnums/Internals/Methods.cs b/ExtendableEnums.Serialization.Newtonsoft.Json/Internals/Methods.cs similarity index 96% rename from ExtendableEnums/Internals/Methods.cs rename to ExtendableEnums.Serialization.Newtonsoft.Json/Internals/Methods.cs index 63cafed..3e3edff 100644 --- a/ExtendableEnums/Internals/Methods.cs +++ b/ExtendableEnums.Serialization.Newtonsoft.Json/Internals/Methods.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Reflection; -namespace ExtendableEnums.Internals; +namespace Newtonsoft.Json.Internals; internal static class Methods { diff --git a/ExtendableEnums.Serialization.Newtonsoft.Json/JsonSerializerSettingsExtensions.cs b/ExtendableEnums.Serialization.Newtonsoft.Json/JsonSerializerSettingsExtensions.cs new file mode 100644 index 0000000..341dd34 --- /dev/null +++ b/ExtendableEnums.Serialization.Newtonsoft.Json/JsonSerializerSettingsExtensions.cs @@ -0,0 +1,29 @@ +namespace Newtonsoft.Json; + +public static class JsonSerializerSettingsExtensions +{ + /// + /// Adds custom converters required for serializing and deserializing extendable enums + /// to the specified list of JSON converters. + /// + /// + /// Example usage: + /// + /// var settings = new JsonSerializerSettings + /// { + /// Formatting = Formatting.Indented + /// }; + /// + /// // Add the custom converters + /// settings.Converters.AddExtendableEnums(); + /// + /// var json = JsonConvert.SerializeObject(yourObject, settings); + /// var obj = JsonConvert.DeserializeObject<YourType>(json, settings); + /// + /// + public static void AddExtendableEnums(this IList converters) + { + converters.Add(new ExtendableEnumJsonConverter()); + converters.Add(new ExtendableEnumDictionaryJsonConverter()); + } +} \ No newline at end of file diff --git a/ExtendableEnums/Serialization/SystemText/ExtendableEnumDictionaryJsonConverter.cs b/ExtendableEnums.Serialization.System.Text.Json/ExtendableEnumDictionaryJsonConverter.cs similarity index 98% rename from ExtendableEnums/Serialization/SystemText/ExtendableEnumDictionaryJsonConverter.cs rename to ExtendableEnums.Serialization.System.Text.Json/ExtendableEnumDictionaryJsonConverter.cs index 9ae20eb..384e247 100644 --- a/ExtendableEnums/Serialization/SystemText/ExtendableEnumDictionaryJsonConverter.cs +++ b/ExtendableEnums.Serialization.System.Text.Json/ExtendableEnumDictionaryJsonConverter.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace ExtendableEnums.Serialization.SystemText; +namespace ExtendableEnums.Serialization.System.Text.Json; /// /// Converts ExtendableEnum objects to and from JSON. diff --git a/ExtendableEnums/Serialization/SystemText/ExtendableEnumJsonConverter.cs b/ExtendableEnums.Serialization.System.Text.Json/ExtendableEnumJsonConverter.cs similarity index 95% rename from ExtendableEnums/Serialization/SystemText/ExtendableEnumJsonConverter.cs rename to ExtendableEnums.Serialization.System.Text.Json/ExtendableEnumJsonConverter.cs index 4b9750c..5dc3e7e 100644 --- a/ExtendableEnums/Serialization/SystemText/ExtendableEnumJsonConverter.cs +++ b/ExtendableEnums.Serialization.System.Text.Json/ExtendableEnumJsonConverter.cs @@ -1,9 +1,9 @@ using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; -using ExtendableEnums.Internals; +using ExtendableEnums.Serialization.System.Text.Json.Internals; -namespace ExtendableEnums.Serialization.SystemText; +namespace ExtendableEnums.Serialization.System.Text.Json; /// /// Converts ExtendableEnum objects to and from JSON. @@ -90,10 +90,8 @@ public override void Write(Utf8JsonWriter writer, TEnumeration value, JsonSerial { return Activator.CreateInstance(t); } - else - { - return null; - } + + return null; } } } \ No newline at end of file diff --git a/ExtendableEnums.Serialization.System.Text.Json/ExtendableEnums.Serialization.System.Text.Json.csproj b/ExtendableEnums.Serialization.System.Text.Json/ExtendableEnums.Serialization.System.Text.Json.csproj new file mode 100644 index 0000000..bca758a --- /dev/null +++ b/ExtendableEnums.Serialization.System.Text.Json/ExtendableEnums.Serialization.System.Text.Json.csproj @@ -0,0 +1,48 @@ + + + netstandard2.0 + 13.0 + enable + enable + 9.1 + 0 + 9.1 + https://github.com/kyleherzog/ExtendableEnums + true + Enum Enumeration Extendable Class + https://github.com/kyleherzog/ExtendableEnums + Kyle Herzog + A .NET Standard library that provides extendable class based enums. + + + true + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + \ No newline at end of file diff --git a/ExtendableEnums.Serialization.System.Text.Json/Internals/Methods.cs b/ExtendableEnums.Serialization.System.Text.Json/Internals/Methods.cs new file mode 100644 index 0000000..8d62d24 --- /dev/null +++ b/ExtendableEnums.Serialization.System.Text.Json/Internals/Methods.cs @@ -0,0 +1,14 @@ +using System.Collections.Concurrent; +using System.Reflection; + +namespace ExtendableEnums.Serialization.System.Text.Json.Internals; + +internal static class Methods +{ + private static readonly ConcurrentDictionary parseValueOrCreateMethodCache = new(); + + internal static MethodInfo GetParseValueOrCreate(Type type) + { + return parseValueOrCreateMethodCache.GetOrAdd(type, t => t.GetMethod("ParseValueOrCreate", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)); + } +} \ No newline at end of file diff --git a/ExtendableEnums.Serialization.System.Text.Json/JsonSerializerOptionsExtensions.cs b/ExtendableEnums.Serialization.System.Text.Json/JsonSerializerOptionsExtensions.cs new file mode 100644 index 0000000..063e7e0 --- /dev/null +++ b/ExtendableEnums.Serialization.System.Text.Json/JsonSerializerOptionsExtensions.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Serialization; + +namespace ExtendableEnums.Serialization.System.Text.Json; + +public static class JsonSerializerOptionsExtensions +{ + /// + /// Adds custom converters required for serializing and deserializing extendable enums + /// to the specified list of JSON converters. + /// + /// + /// Example usage: + /// + /// var options = new JsonSerializerOptions + /// { + /// PropertyNamingPolicy = JsonNamingPolicy.CamelCase + /// }; + /// + /// // Add the custom converters + /// options.Converters.AddExtendableEnums(); + /// + /// var json = JsonSerializer.Serialize(yourObject, options); + /// var obj = JsonSerializer.Deserialize<YourType>(json, options); + /// + /// + public static void AddExtendableEnums(this IList converters) + { + converters.Add(new ExtendableEnumJsonConverter()); + converters.Add(new ExtendableEnumDictionaryJsonConverter()); + } +} \ No newline at end of file diff --git a/ExtendableEnums.TestHost/Controllers/OData/SampleBooksController.cs b/ExtendableEnums.TestHost/Controllers/OData/SampleBooksController.cs index 6c589c3..7998d78 100644 --- a/ExtendableEnums.TestHost/Controllers/OData/SampleBooksController.cs +++ b/ExtendableEnums.TestHost/Controllers/OData/SampleBooksController.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Routing.Controllers; -using Newtonsoft.Json; namespace ExtendableEnums.TestHost.Controllers.OData; @@ -35,7 +34,7 @@ public IActionResult Get(string id) [EnableQuery] public IActionResult Post([FromBody] JsonElement json) { - var book = JsonConvert.DeserializeObject(json.GetRawText()); + var book = JsonSerializer.Deserialize(json.GetRawText()); if (book is null) { diff --git a/ExtendableEnums.TestHost/Startup.cs b/ExtendableEnums.TestHost/Startup.cs index dd1dd66..0513503 100644 --- a/ExtendableEnums.TestHost/Startup.cs +++ b/ExtendableEnums.TestHost/Startup.cs @@ -1,5 +1,6 @@ using ExtendableEnums.Microsoft.AspNetCore; using ExtendableEnums.Microsoft.AspNetCore.OData; +using ExtendableEnums.Serialization.System.Text.Json; using ExtendableEnums.Testing.Models; using Microsoft.AspNetCore.OData; using Microsoft.OData.Edm; @@ -37,15 +38,27 @@ public static void Configure(IApplicationBuilder app) public void ConfigureServices(IServiceCollection services) { Console.WriteLine(Configuration); - services.AddControllers().AddOData(opt => - { - opt.Select().Expand().Filter().OrderBy().SetMaxTop(100).Count(); - opt.AddRouteComponents("odata", GetEdmModel()); - }); - services.AddMvc(options => - { - options.UseExtendableEnumModelBinding(); - }); + + services.AddControllers() + .AddOData(opt => + { + opt.Select().Expand().Filter().OrderBy().SetMaxTop(100).Count(); + opt.AddRouteComponents("odata", GetEdmModel()); + }) + .AddJsonOptions(options => + { + options.JsonSerializerOptions.Converters.AddExtendableEnums(); + }); + + services + .AddMvc(options => + { + options.UseExtendableEnumModelBinding(); + }) + .AddJsonOptions(options => + { + options.JsonSerializerOptions.Converters.AddExtendableEnums(); + }); } private static IEdmModel GetEdmModel() diff --git a/ExtendableEnums.Testing.Models/ExtendableEnums.Testing.Models.csproj b/ExtendableEnums.Testing.Models/ExtendableEnums.Testing.Models.csproj index 4e14221..6a839a8 100644 --- a/ExtendableEnums.Testing.Models/ExtendableEnums.Testing.Models.csproj +++ b/ExtendableEnums.Testing.Models/ExtendableEnums.Testing.Models.csproj @@ -33,6 +33,8 @@ + + diff --git a/ExtendableEnums.Testing.Models/SampleStatus.cs b/ExtendableEnums.Testing.Models/SampleStatus.cs index f01112a..76bfc64 100644 --- a/ExtendableEnums.Testing.Models/SampleStatus.cs +++ b/ExtendableEnums.Testing.Models/SampleStatus.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations.Schema; -using ExtendableEnums.Serialization.SystemText; +using ExtendableEnums.Serialization.System.Text.Json; namespace ExtendableEnums.Testing.Models; [System.Text.Json.Serialization.JsonConverter(typeof(ExtendableEnumJsonConverter))] +[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.ExtendableEnumJsonConverter))] public class SampleStatus : ExtendableEnums.ExtendableEnum { public static readonly SampleStatus Active = new(1, nameof(Active), "ACT"); diff --git a/ExtendableEnums.Testing.Models/SampleStatusByString.cs b/ExtendableEnums.Testing.Models/SampleStatusByString.cs index 6a47542..1499739 100644 --- a/ExtendableEnums.Testing.Models/SampleStatusByString.cs +++ b/ExtendableEnums.Testing.Models/SampleStatusByString.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; -using ExtendableEnums.Serialization.SystemText; +using ExtendableEnums.Serialization.System.Text.Json; namespace ExtendableEnums.Testing.Models; diff --git a/ExtendableEnums.Testing/TestingHost.cs b/ExtendableEnums.Testing/TestingHost.cs index e867d95..4dda5f3 100644 --- a/ExtendableEnums.Testing/TestingHost.cs +++ b/ExtendableEnums.Testing/TestingHost.cs @@ -132,7 +132,6 @@ private void GetNewWebHostInternal() .UseContentRoot(GetSolutionRelativeContentRoot(SolutionRelativeRootPath)) .UseStartup(StartupType); }) - .Build(); host.Start(); diff --git a/ExtendableEnums.UnitTests/ExtendableEnumDictionaryTests/DeserializeShould.cs b/ExtendableEnums.UnitTests/ExtendableEnumDictionaryTests/DeserializeShould.cs index 5d639ad..00c2bf9 100644 --- a/ExtendableEnums.UnitTests/ExtendableEnumDictionaryTests/DeserializeShould.cs +++ b/ExtendableEnums.UnitTests/ExtendableEnumDictionaryTests/DeserializeShould.cs @@ -8,6 +8,19 @@ namespace ExtendableEnums.UnitTests.ExtendableEnumDictionaryTests; [TestClass] public class DeserializeShould { + private JsonSerializerSettings options = new(); + + [TestInitialize] + public void TestInitialize() + { + var options = new JsonSerializerSettings + { + Formatting = Formatting.Indented + }; + options.Converters.AddExtendableEnums(); + this.options = options; + } + [TestMethod] public void DeserializeGivenValidSerialized() { @@ -19,7 +32,7 @@ public void DeserializeGivenValidSerialized() { SampleStatus.Deleted, nameof(SampleStatus.Deleted) }, }; - var result = JsonConvert.DeserializeObject>(serialized); + var result = JsonConvert.DeserializeObject>(serialized, this.options); result.Should().BeEquivalentTo(expected); } @@ -35,7 +48,7 @@ public void DeserializeGivenValidSerializedByString() { SampleStatusByString.Deleted, nameof(SampleStatusByString.Deleted) }, }; - var result = JsonConvert.DeserializeObject>(serialized); + var result = JsonConvert.DeserializeObject>(serialized, this.options); result.Should().BeEquivalentTo(expected); } @@ -52,7 +65,7 @@ public void DeserializeGivenDisplayNameSerializedByString() { SampleStatusByString.Deleted, nameof(SampleStatusByString.Deleted) }, }; - var result = JsonConvert.DeserializeObject>(serialized); + var result = JsonConvert.DeserializeObject>(serialized, this.options); result.Should().BeEquivalentTo(expected); } @@ -69,7 +82,7 @@ public void DeserializeGivenDisplayNameSerialized() { SampleStatus.Deleted, nameof(SampleStatus.Deleted) }, }; - var result = JsonConvert.DeserializeObject>(serialized); + var result = JsonConvert.DeserializeObject>(serialized, this.options); result.Should().BeEquivalentTo(expected); } diff --git a/ExtendableEnums.UnitTests/ExtendableEnumDictionaryTests/SerializeShould.cs b/ExtendableEnums.UnitTests/ExtendableEnumDictionaryTests/SerializeShould.cs index fdf9fc1..aaa4c1b 100644 --- a/ExtendableEnums.UnitTests/ExtendableEnumDictionaryTests/SerializeShould.cs +++ b/ExtendableEnums.UnitTests/ExtendableEnumDictionaryTests/SerializeShould.cs @@ -7,6 +7,19 @@ namespace ExtendableEnums.UnitTests.ExtendableEnumDictionaryTests; [TestClass] public class SerializeShould { + private JsonSerializerSettings options = new(); + + [TestInitialize] + public void TestInitialize() + { + var options = new JsonSerializerSettings + { + Formatting = Formatting.None + }; + options.Converters.AddExtendableEnums(); + this.options = options; + } + [TestMethod] public void SerializeKeyAsValueOnlyGivenIntValue() { @@ -16,7 +29,7 @@ public void SerializeKeyAsValueOnlyGivenIntValue() { SampleStatus.Deleted, nameof(SampleStatus.Deleted) }, }; - var serialized = JsonConvert.SerializeObject(items); + var serialized = JsonConvert.SerializeObject(items, this.options); Assert.AreEqual("{\"1\":\"Active\",\"2\":\"Deleted\"}", serialized); } @@ -29,7 +42,7 @@ public void SerializeKeyAsValueOnlyGivenStringValue() { SampleStatusByString.Deleted, nameof(SampleStatusByString.Deleted) }, }; - var serialized = JsonConvert.SerializeObject(items); + var serialized = JsonConvert.SerializeObject(items, this.options); Assert.AreEqual("{\"B\":\"Active\",\"C\":\"Deleted\"}", serialized); } } \ No newline at end of file diff --git a/ExtendableEnums.UnitTests/ExtendableEnumTests/NewsonsoftSerializationShould.cs b/ExtendableEnums.UnitTests/ExtendableEnumTests/NewsonsoftSerializationShould.cs index a2ba3e8..29f08d4 100644 --- a/ExtendableEnums.UnitTests/ExtendableEnumTests/NewsonsoftSerializationShould.cs +++ b/ExtendableEnums.UnitTests/ExtendableEnumTests/NewsonsoftSerializationShould.cs @@ -7,46 +7,59 @@ namespace ExtendableEnums.UnitTests.ExpandableEnumerationTests; [TestClass] public class NewsonsoftSerializationShould { + private JsonSerializerSettings options = new(); + + [TestInitialize] + public void TestInitialize() + { + var options = new JsonSerializerSettings + { + Formatting = Formatting.Indented + }; + options.Converters.AddExtendableEnums(); + this.options = options; + } + [TestMethod] public void DeserializeFromNull() { - var nullSerialized = JsonConvert.SerializeObject(null); - var status = JsonConvert.DeserializeObject(nullSerialized); + var nullSerialized = JsonConvert.SerializeObject(null, this.options); + var status = JsonConvert.DeserializeObject(nullSerialized, this.options); Assert.IsNull(status); } [TestMethod] public void DeserializeFromObjectGivenNumericValuePripertyNotDeclaredInPrimaryType() { - var status = JsonConvert.DeserializeObject($"{{\"value\" : \"{SampleStatusDeclared.Pending.Value}\"}}"); + var status = JsonConvert.DeserializeObject($"{{\"value\" : \"{SampleStatusDeclared.Pending.Value}\"}}", this.options); Assert.AreEqual(SampleStatusDeclared.Pending, status); } [TestMethod] public void DeserializeFromObjectWithNoValuePropertyToDefaultValue() { - var status = JsonConvert.DeserializeObject($"{{\"id\" : \"{SampleStatus.Inactive.Value}\"}}"); + var status = JsonConvert.DeserializeObject($"{{\"id\" : \"{SampleStatus.Inactive.Value}\"}}", this.options); Assert.AreEqual(SampleStatus.Unknown, status); } [TestMethod] public void DeserializeFromObjectWithNumericValueProperty() { - var status = JsonConvert.DeserializeObject($"{{\"value\" : {SampleStatus.Inactive.Value}}}"); + var status = JsonConvert.DeserializeObject($"{{\"value\" : {SampleStatus.Inactive.Value}}}", this.options); Assert.AreEqual(SampleStatus.Inactive, status); } [TestMethod] public void DeserializeFromObjectWithStringValueProperty() { - var status = JsonConvert.DeserializeObject($"{{\"value\" : \"{SampleStatusByString.Inactive.Value}\"}}"); + var status = JsonConvert.DeserializeObject($"{{\"value\" : \"{SampleStatusByString.Inactive.Value}\"}}", this.options); Assert.AreEqual(SampleStatusByString.Inactive, status); } [TestMethod] public void DeserializeFromObjectNotDefined() { - var status = JsonConvert.DeserializeObject("{\"value\" : -123}"); + var status = JsonConvert.DeserializeObject("{\"value\" : -123}", this.options); Assert.AreEqual(-123, status?.Value); } @@ -58,14 +71,14 @@ public void DeserializeFromSerializedDictionaryGivenExtendedEnumIsKey() dictionary.Add(keyStatus, "test value"); var serialized = JsonConvert.SerializeObject(dictionary); Console.WriteLine(serialized); - var deserialized = JsonConvert.DeserializeObject>(serialized); + var deserialized = JsonConvert.DeserializeObject>(serialized, this.options); Assert.AreEqual(dictionary[keyStatus], deserialized?[keyStatus]); } [TestMethod] public void DeserializeFromTheValueOnly() { - var status = JsonConvert.DeserializeObject($"{SampleStatus.Inactive.Value}"); + var status = JsonConvert.DeserializeObject($"{SampleStatus.Inactive.Value}", this.options); Assert.AreEqual(SampleStatus.Inactive, status); } @@ -73,7 +86,7 @@ public void DeserializeFromTheValueOnly() public void SerializeTheValueOnly() { var status = SampleStatus.Active; - var serialized = JsonConvert.SerializeObject(status); + var serialized = JsonConvert.SerializeObject(status, this.options); Assert.AreEqual($"{status.Value}", serialized); } @@ -82,7 +95,7 @@ public void SerializeTheValueOnly() public void SerializeToNull() { SampleStatus? status = null; - var serialized = JsonConvert.SerializeObject(status); + var serialized = JsonConvert.SerializeObject(status, this.options); Assert.AreEqual("null", serialized); } } \ No newline at end of file diff --git a/ExtendableEnums.UnitTests/ExtendableEnumTests/SystemTextSerializationShould.cs b/ExtendableEnums.UnitTests/ExtendableEnumTests/SystemTextSerializationShould.cs index 58d9a2d..5f7ad03 100644 --- a/ExtendableEnums.UnitTests/ExtendableEnumTests/SystemTextSerializationShould.cs +++ b/ExtendableEnums.UnitTests/ExtendableEnumTests/SystemTextSerializationShould.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using ExtendableEnums.Serialization.System.Text.Json; using ExtendableEnums.Testing.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -7,46 +8,59 @@ namespace ExtendableEnums.UnitTests.ExtendableEnumerationTests; [TestClass] public class SystemTextSerializationShould { + private JsonSerializerOptions options = new(); + + [TestInitialize] + public void TestInitialize() + { + var options = new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + options.Converters.AddExtendableEnums(); + this.options = options; + } + [TestMethod] public void DeserializeFromNull() { - var nullSerialized = JsonSerializer.Serialize(null); - var status = JsonSerializer.Deserialize(nullSerialized); + var nullSerialized = JsonSerializer.Serialize(null, this.options); + var status = JsonSerializer.Deserialize(nullSerialized, this.options); Assert.IsNull(status); } [TestMethod] public void DeserializeFromObjectGivenNumericValuePripertyNotDeclaredInPrimaryType() { - var status = JsonSerializer.Deserialize($"{{\"value\" : \"{SampleStatusDeclared.Pending.Value}\"}}"); + var status = JsonSerializer.Deserialize($"{{\"value\" : \"{SampleStatusDeclared.Pending.Value}\"}}", this.options); Assert.AreEqual(SampleStatusDeclared.Pending, status); } [TestMethod] public void DeserializeFromObjectWithNoValuePropertyToDefaultValue() { - var status = JsonSerializer.Deserialize($"{{\"id\" : \"{SampleStatus.Inactive.Value}\"}}"); + var status = JsonSerializer.Deserialize($"{{\"id\" : \"{SampleStatus.Inactive.Value}\"}}", this.options); Assert.AreEqual(SampleStatus.Unknown, status); } [TestMethod] public void DeserializeFromObjectWithNumericValueProperty() { - var status = JsonSerializer.Deserialize($"{{\"value\" : {SampleStatus.Inactive.Value}}}"); + var status = JsonSerializer.Deserialize($"{{\"value\" : {SampleStatus.Inactive.Value}}}", this.options); Assert.AreEqual(SampleStatus.Inactive, status); } [TestMethod] public void DeserializeFromObjectWithStringValueProperty() { - var status = JsonSerializer.Deserialize($"{{\"value\" : \"{SampleStatusByString.Inactive.Value}\"}}"); + var status = JsonSerializer.Deserialize($"{{\"value\" : \"{SampleStatusByString.Inactive.Value}\"}}", this.options); Assert.AreEqual(SampleStatusByString.Inactive, status); } [TestMethod] public void DeserializeFromObjectNotDefined() { - var status = JsonSerializer.Deserialize("{\"value\" : -123}"); + var status = JsonSerializer.Deserialize("{\"value\" : -123}", this.options); Assert.AreEqual(-123, status?.Value); } @@ -56,16 +70,16 @@ public void DeserializeFromSerializedDictionaryGivenExtendedEnumIsKey() var dictionary = new ExtendableEnumDictionary(); var keyStatus = SampleStatus.Discontinued; dictionary.Add(keyStatus, "test value"); - var serialized = JsonSerializer.Serialize(dictionary); + var serialized = JsonSerializer.Serialize(dictionary, this.options); Console.WriteLine(serialized); - var deserialized = JsonSerializer.Deserialize>(serialized); + var deserialized = JsonSerializer.Deserialize>(serialized, this.options); Assert.AreEqual(dictionary[keyStatus], deserialized?[keyStatus]); } [TestMethod] public void DeserializeFromTheValueOnly() { - var status = JsonSerializer.Deserialize($"{SampleStatus.Inactive.Value}"); + var status = JsonSerializer.Deserialize($"{SampleStatus.Inactive.Value}", this.options); Assert.AreEqual(SampleStatus.Inactive, status); } @@ -73,7 +87,7 @@ public void DeserializeFromTheValueOnly() public void SerializeTheValueOnly() { var status = SampleStatus.Active; - var serialized = JsonSerializer.Serialize(status); + var serialized = JsonSerializer.Serialize(status, this.options); Assert.AreEqual($"{status.Value}", serialized); } @@ -82,7 +96,7 @@ public void SerializeTheValueOnly() public void SerializeToNull() { SampleStatus? status = null; - var serialized = JsonSerializer.Serialize(status); + var serialized = JsonSerializer.Serialize(status, this.options); Assert.AreEqual("null", serialized); } } \ No newline at end of file diff --git a/ExtendableEnums.UnitTests/ExtendableEnums.UnitTests.csproj b/ExtendableEnums.UnitTests/ExtendableEnums.UnitTests.csproj index ff5d046..1bd5cf4 100644 --- a/ExtendableEnums.UnitTests/ExtendableEnums.UnitTests.csproj +++ b/ExtendableEnums.UnitTests/ExtendableEnums.UnitTests.csproj @@ -33,6 +33,7 @@ + diff --git a/ExtendableEnums.sln b/ExtendableEnums.sln index 118930d..3f1b0dd 100644 --- a/ExtendableEnums.sln +++ b/ExtendableEnums.sln @@ -41,6 +41,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExtendableEnums.EntityFrame EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtendableEnums.Demo.Wasm", "ExtendableEnums.Demo.Wasm\ExtendableEnums.Demo.Wasm.csproj", "{D552BEE7-F8FD-4967-AFBF-BA0E054AD6CA}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExtendableEnums.Serialization.Newtonsoft.Json", "ExtendableEnums.Serialization.Newtonsoft.Json\ExtendableEnums.Serialization.Newtonsoft.Json.csproj", "{19AC125D-474C-4C01-B36E-3CE6EE1E3864}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExtendableEnums.Serialization.System.Text.Json", "ExtendableEnums.Serialization.System.Text.Json\ExtendableEnums.Serialization.System.Text.Json.csproj", "{4AECE989-A952-4295-841F-3A40583FBE68}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -107,6 +111,14 @@ Global {D552BEE7-F8FD-4967-AFBF-BA0E054AD6CA}.Debug|Any CPU.Build.0 = Debug|Any CPU {D552BEE7-F8FD-4967-AFBF-BA0E054AD6CA}.Release|Any CPU.ActiveCfg = Release|Any CPU {D552BEE7-F8FD-4967-AFBF-BA0E054AD6CA}.Release|Any CPU.Build.0 = Release|Any CPU + {19AC125D-474C-4C01-B36E-3CE6EE1E3864}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19AC125D-474C-4C01-B36E-3CE6EE1E3864}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19AC125D-474C-4C01-B36E-3CE6EE1E3864}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19AC125D-474C-4C01-B36E-3CE6EE1E3864}.Release|Any CPU.Build.0 = Release|Any CPU + {4AECE989-A952-4295-841F-3A40583FBE68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AECE989-A952-4295-841F-3A40583FBE68}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AECE989-A952-4295-841F-3A40583FBE68}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AECE989-A952-4295-841F-3A40583FBE68}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ExtendableEnums/ExtendableEnum.cs b/ExtendableEnums/ExtendableEnum.cs index 98c6025..6a5f4f2 100644 --- a/ExtendableEnums/ExtendableEnum.cs +++ b/ExtendableEnums/ExtendableEnum.cs @@ -12,7 +12,6 @@ public abstract class ExtendableEnum : ExtendableEnumBase /// The unique value that represents this enumeration value. /// The value that represents its display name. - [System.Text.Json.Serialization.JsonConstructor] protected ExtendableEnum(int value, string displayName) : base(value, displayName) { diff --git a/ExtendableEnums/ExtendableEnumBase.cs b/ExtendableEnums/ExtendableEnumBase.cs index 749a5a1..5f8b5ac 100644 --- a/ExtendableEnums/ExtendableEnumBase.cs +++ b/ExtendableEnums/ExtendableEnumBase.cs @@ -1,8 +1,6 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using System.Reflection; -using ExtendableEnums.Serialization.Newtonsoft; -using Newtonsoft.Json; namespace ExtendableEnums; @@ -11,7 +9,6 @@ namespace ExtendableEnums; /// /// The of this enumeration (itself). /// The of the value property. -[JsonConverter(typeof(ExtendableEnumJsonConverter))] [TypeConverter(typeof(ExtendableEnumTypeConverter))] public abstract class ExtendableEnumBase : IExtendableEnum, IComparable, IComparable, IEquatable where TEnumeration : ExtendableEnumBase diff --git a/ExtendableEnums/ExtendableEnumDictionary.cs b/ExtendableEnums/ExtendableEnumDictionary.cs index e74a514..90a3d2f 100644 --- a/ExtendableEnums/ExtendableEnumDictionary.cs +++ b/ExtendableEnums/ExtendableEnumDictionary.cs @@ -1,7 +1,5 @@ using System.Diagnostics; using System.Runtime.Serialization; -using ExtendableEnums.Serialization.Newtonsoft; -using Newtonsoft.Json; namespace ExtendableEnums; @@ -11,8 +9,6 @@ namespace ExtendableEnums; /// /// The type of the key. /// The type of the value. -[System.Text.Json.Serialization.JsonConverter(typeof(ExtendableEnums.Serialization.SystemText.ExtendableEnumDictionaryJsonConverter))] -[JsonConverter(typeof(ExtendableEnumDictionaryJsonConverter))] [Serializable] public class ExtendableEnumDictionary : Dictionary where TKey : IExtendableEnum diff --git a/ExtendableEnums/ExtendableEnums.csproj b/ExtendableEnums/ExtendableEnums.csproj index 97206a5..a81cfcb 100644 --- a/ExtendableEnums/ExtendableEnums.csproj +++ b/ExtendableEnums/ExtendableEnums.csproj @@ -27,7 +27,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers @@ -37,7 +36,6 @@ runtime; build; native; contentfiles; analyzers - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ExtendableEnums/TypeExtensions.cs b/ExtendableEnums/TypeExtensions.cs index adcb535..6320c30 100644 --- a/ExtendableEnums/TypeExtensions.cs +++ b/ExtendableEnums/TypeExtensions.cs @@ -47,7 +47,7 @@ public static bool IsExtendableEnumDictionary(this Type type) return IsTypeDerivedFromGenericType(type, typeof(ExtendableEnumDictionary<,>)); } - internal static Type[] GetExtendableEnumArgs(this Type type) + public static Type[] GetExtendableEnumArgs(this Type type) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ExtendableEnumBase<,>)) { @@ -55,12 +55,12 @@ internal static Type[] GetExtendableEnumArgs(this Type type) return args; } - if (type.BaseType != typeof(object)) + if (type.BaseType is { } baseType && baseType != typeof(object)) { - return GetExtendableEnumArgs(type.BaseType); + return GetExtendableEnumArgs(baseType); } - return Array.Empty(); + return []; } private static bool IsTypeDerivedFromGenericType(Type typeToCheck, Type genericType) @@ -69,17 +69,17 @@ private static bool IsTypeDerivedFromGenericType(Type typeToCheck, Type genericT { return false; } - else if (typeToCheck is null) + + if (typeToCheck is null) { return false; } - else if (typeToCheck.IsGenericType && typeToCheck.GetGenericTypeDefinition() == genericType) + + if (typeToCheck.IsGenericType && typeToCheck.GetGenericTypeDefinition() == genericType) { return true; } - else - { - return IsTypeDerivedFromGenericType(typeToCheck.BaseType, genericType); - } + + return IsTypeDerivedFromGenericType(typeToCheck.BaseType, genericType); } } \ No newline at end of file