Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/SmartEnum.SystemTextJson/SmartEnumNameConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
45 changes: 45 additions & 0 deletions src/SmartEnum.SystemTextJson/SmartEnumValueConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,6 +25,10 @@ public class TestClass

[JsonConverter(typeof(SmartEnumNameConverter<TestEnumString, string>))]
public TestEnumString String { get; set; }

public IDictionary<TestEnumInt32, string> DictInt32String { get; set; }

public IDictionary<TestEnumString, string> DictStringString { get; set; }
}

static readonly TestClass TestInstance = new TestClass
Expand All @@ -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
Expand All @@ -41,27 +48,31 @@ 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);
}

[Fact]
public void DeserializesNames()
{
var obj = JsonSerializer.Deserialize<TestClass>(JsonString);
var obj = JsonSerializer.Deserialize<TestClass>(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]
Expand All @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,6 +25,10 @@ public class TestClass

[JsonConverter(typeof(SmartEnumValueConverter<TestEnumString, string>))]
public TestEnumString String { get; set; }

public IDictionary<TestEnumInt32, string> DictInt32String { get; set; }

public IDictionary<TestEnumString, string> DictStringString { get; set; }
}

static readonly TestClass TestInstance = new TestClass
Expand All @@ -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
Expand All @@ -41,27 +48,31 @@ 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);
}

[Fact]
public void DeserializesValue()
{
var obj = JsonSerializer.Deserialize<TestClass>(JsonString);
var obj = JsonSerializer.Deserialize<TestClass>(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]
Expand All @@ -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]
Expand Down
54 changes: 53 additions & 1 deletion test/SmartEnum.SystemTextJson.UnitTests/TestEnums.cs
Original file line number Diff line number Diff line change
@@ -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<TestEnumBoolean, bool>
{
Expand Down Expand Up @@ -34,4 +38,52 @@ public sealed class TestEnumString : SmartEnum<TestEnumString, string>

TestEnumString(string name, string value) : base(name, value) { }
}

public sealed class TestJsonConverters
{
public static readonly JsonSerializerOptions NameConverterOptions = new()
{
Converters =
{
new SmartEnumNameConverter<TestEnumInt32, int>(),
new SmartEnumNameConverter<TestEnumString, string>()
},
WriteIndented = true
};

public static readonly JsonSerializerOptions ValueConverterOptions = new()
{
Converters =
{
new SmartEnumValueConverter<TestEnumInt32, int>(),
new SmartEnumValueConverter<TestEnumString, string>()
},
WriteIndented = true
};

}

public sealed class TestDictInt32EnumString
{
public static readonly IDictionary<TestEnumInt32, string> Instance = new Dictionary<TestEnumInt32, string>
{ { TestEnumInt32.Instance, nameof(Instance) } };
}

public sealed class TestDictStringEnumString
{
public static readonly IDictionary<TestEnumString, string> Instance = new Dictionary<TestEnumString, string>
{ { 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);
}
}