diff --git a/src/SmartEnum.SystemTextJson/SmartEnumNameConverter.cs b/src/SmartEnum.SystemTextJson/SmartEnumNameConverter.cs index b3a37ef7..79321c2b 100644 --- a/src/SmartEnum.SystemTextJson/SmartEnumNameConverter.cs +++ b/src/SmartEnum.SystemTextJson/SmartEnumNameConverter.cs @@ -39,5 +39,22 @@ private TEnum GetFromName(string name) throw new JsonException($"Error converting value '{name}' to a smart enum.", ex); } } + + public override TEnum ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.PropertyName: + return GetFromName(reader.GetString()); + + default: + throw new JsonException($"Unexpected token {reader.TokenType} when parsing a dictionary with smart enum key."); + } + } + + public override void WriteAsPropertyName(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WritePropertyName(value.Name); + } } } \ No newline at end of file diff --git a/src/SmartEnum.SystemTextJson/SmartEnumValueConverter.cs b/src/SmartEnum.SystemTextJson/SmartEnumValueConverter.cs index 601b9727..cee03350 100644 --- a/src/SmartEnum.SystemTextJson/SmartEnumValueConverter.cs +++ b/src/SmartEnum.SystemTextJson/SmartEnumValueConverter.cs @@ -83,5 +83,50 @@ private TValue ReadValue(ref Utf8JsonReader reader) throw new ArgumentOutOfRangeException(typeof(TValue).ToString(), $"{typeof(TValue).Name} is not supported."); } + + public override TEnum ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new ArgumentException( + $"Unexpected token {reader.TokenType} when parsing a dictionary with smart enum key."); + } + return GetFromValue(ReadPropertyName(ref reader)); + } + + public override void WriteAsPropertyName(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WritePropertyName(value.Value?.ToString() ?? string.Empty); + } + + private TValue ReadPropertyName(ref Utf8JsonReader reader) + { + if (typeof(TValue) == typeof(bool)) + return (TValue)(object)Convert.ToBoolean(reader.GetString()); + if (typeof(TValue) == typeof(byte)) + return (TValue)(object)Convert.ToByte(reader.GetString()); + if (typeof(TValue) == typeof(sbyte)) + return (TValue)(object)Convert.ToSByte(reader.GetString()); + if (typeof(TValue) == typeof(short)) + return (TValue)(object)Convert.ToInt16(reader.GetString()); + if (typeof(TValue) == typeof(ushort)) + return (TValue)(object)Convert.ToUInt16(reader.GetString()); + if (typeof(TValue) == typeof(int)) + return (TValue)(object)Convert.ToInt32(reader.GetString()); + if (typeof(TValue) == typeof(uint)) + return (TValue)(object)Convert.ToUInt32(reader.GetString()); + if (typeof(TValue) == typeof(long)) + return (TValue)(object)Convert.ToInt64(reader.GetString()); + if (typeof(TValue) == typeof(ulong)) + return (TValue)(object)Convert.ToUInt64(reader.GetString()); + if (typeof(TValue) == typeof(float)) + return (TValue)(object)Convert.ToSingle(reader.GetString()); + if (typeof(TValue) == typeof(double)) + return (TValue)(object)Convert.ToDouble(reader.GetString()); + if (typeof(TValue) == typeof(string)) + return (TValue)(object)reader.GetString(); + + throw new ArgumentOutOfRangeException(typeof(TValue).ToString(), $"{typeof(TValue).Name} is not supported."); + } } } \ No newline at end of file diff --git a/test/SmartEnum.SystemTextJson.UnitTests/SmartEnumNameConverterTests.cs b/test/SmartEnum.SystemTextJson.UnitTests/SmartEnumNameConverterTests.cs index f84c49ad..720ad253 100644 --- a/test/SmartEnum.SystemTextJson.UnitTests/SmartEnumNameConverterTests.cs +++ b/test/SmartEnum.SystemTextJson.UnitTests/SmartEnumNameConverterTests.cs @@ -2,6 +2,7 @@ namespace Ardalis.SmartEnum.SystemTextJson.UnitTests { using FluentAssertions; using System; + using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; using Xunit; @@ -24,6 +25,10 @@ public class TestClass [JsonConverter(typeof(SmartEnumNameConverter))] public TestEnumString String { get; set; } + + public IDictionary DictInt32String { get; set; } + + public IDictionary DictStringString { get; set; } } static readonly TestClass TestInstance = new TestClass @@ -33,6 +38,8 @@ public class TestClass Int32 = TestEnumInt32.Instance, Double = TestEnumDouble.Instance, String = TestEnumString.Instance, + DictInt32String = TestDictInt32EnumString.Instance, + DictStringString = TestDictStringEnumString.Instance }; static readonly string JsonString = JsonSerializer.Serialize(new @@ -41,13 +48,15 @@ public class TestClass Int16 = "Instance", Int32 = "Instance", Double = "Instance", - String = "Instance" - }, new JsonSerializerOptions { WriteIndented = true }); + String = "Instance", + DictInt32String = new { Instance = "Instance" }, + DictStringString = new { Instance = "Instance" } + }, TestJsonConverters.NameConverterOptions); [Fact] public void SerializesNames() { - var json = JsonSerializer.Serialize(TestInstance, new JsonSerializerOptions { WriteIndented = true }); + var json = JsonSerializer.Serialize(TestInstance, TestJsonConverters.NameConverterOptions); json.Should().Be(JsonString); } @@ -55,13 +64,15 @@ public void SerializesNames() [Fact] public void DeserializesNames() { - var obj = JsonSerializer.Deserialize(JsonString); + var obj = JsonSerializer.Deserialize(JsonString, TestJsonConverters.NameConverterOptions); obj.Bool.Should().BeSameAs(TestEnumBoolean.Instance); obj.Int16.Should().BeSameAs(TestEnumInt16.Instance); obj.Int32.Should().BeSameAs(TestEnumInt32.Instance); obj.Double.Should().BeSameAs(TestEnumDouble.Instance); obj.String.Should().BeSameAs(TestEnumString.Instance); + obj.DictInt32String.Should().BeEquivalentTo(TestDictInt32EnumString.Instance); + obj.DictStringString.Should().BeEquivalentTo(TestDictStringEnumString.Instance); } [Fact] @@ -76,6 +87,8 @@ public void DeserializesNullByDefault() obj.Int32.Should().BeNull(); obj.Double.Should().BeNull(); obj.String.Should().BeNull(); + obj.DictInt32String.Should().BeNull(); + obj.DictStringString.Should().BeNull(); } [Fact] diff --git a/test/SmartEnum.SystemTextJson.UnitTests/SmartEnumValueConverterTests.cs b/test/SmartEnum.SystemTextJson.UnitTests/SmartEnumValueConverterTests.cs index 64b49720..d5aa08dc 100644 --- a/test/SmartEnum.SystemTextJson.UnitTests/SmartEnumValueConverterTests.cs +++ b/test/SmartEnum.SystemTextJson.UnitTests/SmartEnumValueConverterTests.cs @@ -2,6 +2,7 @@ namespace Ardalis.SmartEnum.SystemTextJson.UnitTests { using FluentAssertions; using System; + using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; using Xunit; @@ -24,6 +25,10 @@ public class TestClass [JsonConverter(typeof(SmartEnumValueConverter))] public TestEnumString String { get; set; } + + public IDictionary DictInt32String { get; set; } + + public IDictionary DictStringString { get; set; } } static readonly TestClass TestInstance = new TestClass @@ -33,6 +38,8 @@ public class TestClass Int32 = TestEnumInt32.Instance, Double = TestEnumDouble.Instance, String = TestEnumString.Instance, + DictInt32String = TestDictInt32EnumString.Instance, + DictStringString = TestDictStringEnumString.Instance }; static readonly string JsonString = JsonSerializer.Serialize(new @@ -41,13 +48,15 @@ public class TestClass Int16 = 1, Int32 = 1, Double = 1.2, - String = "1.5" - }, new JsonSerializerOptions { WriteIndented = true }); + String = "1.5", + DictInt32String = new DictInt32EnumStringJson(), + DictStringString = new DictStringEnumStringJson() + }, TestJsonConverters.ValueConverterOptions); [Fact] public void SerializesValue() { - var json = JsonSerializer.Serialize(TestInstance, new JsonSerializerOptions { WriteIndented = true }); + var json = JsonSerializer.Serialize(TestInstance, TestJsonConverters.ValueConverterOptions); json.Should().Be(JsonString); } @@ -55,13 +64,15 @@ public void SerializesValue() [Fact] public void DeserializesValue() { - var obj = JsonSerializer.Deserialize(JsonString); + var obj = JsonSerializer.Deserialize(JsonString, TestJsonConverters.ValueConverterOptions); obj.Bool.Should().BeSameAs(TestEnumBoolean.Instance); obj.Int16.Should().BeSameAs(TestEnumInt16.Instance); obj.Int32.Should().BeSameAs(TestEnumInt32.Instance); obj.Double.Should().BeSameAs(TestEnumDouble.Instance); obj.String.Should().BeSameAs(TestEnumString.Instance); + obj.DictInt32String.Should().BeEquivalentTo(TestDictInt32EnumString.Instance); + obj.DictStringString.Should().BeEquivalentTo(TestDictStringEnumString.Instance); } [Fact] @@ -76,6 +87,8 @@ public void DeserializesNullByDefault() obj.Int32.Should().BeNull(); obj.Double.Should().BeNull(); obj.String.Should().BeNull(); + obj.DictInt32String.Should().BeNull(); + obj.DictStringString.Should().BeNull(); } [Fact] diff --git a/test/SmartEnum.SystemTextJson.UnitTests/TestEnums.cs b/test/SmartEnum.SystemTextJson.UnitTests/TestEnums.cs index d30a8b2b..739820aa 100644 --- a/test/SmartEnum.SystemTextJson.UnitTests/TestEnums.cs +++ b/test/SmartEnum.SystemTextJson.UnitTests/TestEnums.cs @@ -1,4 +1,8 @@ -namespace Ardalis.SmartEnum.SystemTextJson.UnitTests +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Ardalis.SmartEnum.SystemTextJson.UnitTests { public sealed class TestEnumBoolean : SmartEnum { @@ -34,4 +38,52 @@ public sealed class TestEnumString : SmartEnum TestEnumString(string name, string value) : base(name, value) { } } + + public sealed class TestJsonConverters + { + public static readonly JsonSerializerOptions NameConverterOptions = new() + { + Converters = + { + new SmartEnumNameConverter(), + new SmartEnumNameConverter() + }, + WriteIndented = true + }; + + public static readonly JsonSerializerOptions ValueConverterOptions = new() + { + Converters = + { + new SmartEnumValueConverter(), + new SmartEnumValueConverter() + }, + WriteIndented = true + }; + + } + + public sealed class TestDictInt32EnumString + { + public static readonly IDictionary Instance = new Dictionary + { { TestEnumInt32.Instance, nameof(Instance) } }; + } + + public sealed class TestDictStringEnumString + { + public static readonly IDictionary Instance = new Dictionary + { { TestEnumString.Instance, nameof(Instance) } }; + } + + public sealed class DictInt32EnumStringJson + { + [JsonPropertyName("1")] + public string Value => nameof(TestDictInt32EnumString.Instance); + } + + public sealed class DictStringEnumStringJson + { + [JsonPropertyName("1.5")] + public string Value => nameof(TestDictInt32EnumString.Instance); + } } \ No newline at end of file