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
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ public JsonStringEnumMemberConverterHelper(JsonNamingPolicy? namingPolicy, bool
string name = builtInNames[i];
FieldInfo field = _EnumType.GetField(name, EnumBindings)!;
EnumMemberAttribute? enumMemberAttribute = field.GetCustomAttribute<EnumMemberAttribute>(true);
string transformedName = enumMemberAttribute?.Value ?? namingPolicy?.ConvertName(name) ?? name;
JsonPropertyNameAttribute? jsonPropertyNameAttribute = field.GetCustomAttribute<JsonPropertyNameAttribute>(true);

string transformedName = enumMemberAttribute?.Value ??
jsonPropertyNameAttribute?.Name ??
namingPolicy?.ConvertName(name) ??
name;

if (enumValue is not TEnum typedValue)
throw new NotSupportedException();
Expand Down
27 changes: 27 additions & 0 deletions ClassLibraries/Macross.Json.Extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,33 @@ but it adds two features and fixes one bug.
}
```

* [JsonPropertyName](https://docs.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonpropertynameattribute)
Support

When serializing and deserializing an Enum as a string the value specified
by `EnumMember` will be used.

```csharp
[JsonConverter(typeof(JsonStringEnumMemberConverter))]
public enum DefinitionType
{
[JsonPropertyName("UNKNOWN_DEFINITION_000")]
DefinitionUnknown
}

[TestMethod]
public void ExampleTest()
{
string Json = JsonSerializer.Serialize(DefinitionType.DefinitionUnknown);

Assert.AreEqual("\"UNKNOWN_DEFINITION_000\"", Json);

DefinitionType ParsedDefinitionType = JsonSerializer.Deserialize<DefinitionType>(Json);

Assert.AreEqual(DefinitionType.DefinitionUnknown, ParsedDefinitionType);
}
```

* Nullable&lt;Enum&gt; Support

If you try to use the built-in `JsonStringEnumConverter` with a nullable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
#if NET5_0

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Macross.Json.Extensions.Tests
{
[TestClass]
public class JsonStringEnumMemberConverterJsonPropertyNameTests
{
[TestMethod]
public void EnumMemberSerializationTest()
{
string Json = JsonSerializer.Serialize(FlagDefinitions.Four);
Assert.AreEqual(@"""four value""", Json);

Json = JsonSerializer.Serialize(FlagDefinitions.Four | FlagDefinitions.One);
Assert.AreEqual(@"""one value, four value""", Json);

Json = JsonSerializer.Serialize((FlagDefinitions)255);
Assert.AreEqual("255", Json);
}

[TestMethod]
public void EnumMemberDeserializationTest()
{
FlagDefinitions Value = JsonSerializer.Deserialize<FlagDefinitions>(@"""all values""");
Assert.AreEqual(FlagDefinitions.All, Value);

Value = JsonSerializer.Deserialize<FlagDefinitions>(@"""two value, three value""");
Assert.AreEqual(FlagDefinitions.Two | FlagDefinitions.Three, Value);

Value = JsonSerializer.Deserialize<FlagDefinitions>(@"""tWo VALUE""");
Assert.AreEqual(FlagDefinitions.Two, Value);
}

[ExpectedException(typeof(JsonException))]
[TestMethod]
public void EnumMemberInvalidTypeDeserializationTest() => JsonSerializer.Deserialize<FlagDefinitions>(@"null");

[ExpectedException(typeof(JsonException))]
[TestMethod]
public void EnumMemberInvalidValueDeserializationTest() => JsonSerializer.Deserialize<FlagDefinitions>(@"""invalid_value""");

[ExpectedException(typeof(JsonException))]
[TestMethod]
public void EnumMemberInvalidNumericValueDeserializationTest()
{
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumMemberConverter(allowIntegerValues: false));

JsonSerializer.Serialize((FlagDefinitions)255, options);
}

[TestMethod]
public void NullableEnumSerializationTest()
{
JsonSerializerOptions Options = new JsonSerializerOptions();
Options.Converters.Add(new JsonStringEnumMemberConverter());

string Json = JsonSerializer.Serialize((DayOfWeek?)null, Options);
Assert.AreEqual("null", Json);

Json = JsonSerializer.Serialize((DayOfWeek?)DayOfWeek.Monday, Options);
Assert.AreEqual(@"""Monday""", Json);

Json = JsonSerializer.Serialize((EnumDefinition?)255, Options);
Assert.AreEqual("255", Json);
}

[TestMethod]
public void NullableEnumDeserializationTest()
{
JsonSerializerOptions Options = new JsonSerializerOptions();
Options.Converters.Add(new JsonStringEnumMemberConverter());

DayOfWeek? Value = JsonSerializer.Deserialize<DayOfWeek?>("null", Options);
Assert.AreEqual(null, Value);

Value = JsonSerializer.Deserialize<DayOfWeek?>(@"""Friday""", Options);
Assert.AreEqual(DayOfWeek.Friday, Value);

EnumDefinition? EnumValue = JsonSerializer.Deserialize<EnumDefinition?>(@"""fIrSt""", Options);
Assert.AreEqual(EnumDefinition.First, EnumValue);

EnumValue = JsonSerializer.Deserialize<EnumDefinition?>(@"255", Options);
Assert.AreEqual(255, (int)EnumValue!);
}

[TestMethod]
public void EnumMemberSerializationOptionsTest()
{
JsonSerializerOptions options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumMemberConverter(JsonNamingPolicy.CamelCase) }
};

string json = JsonSerializer.Serialize(EnumDefinition.First, options);
Assert.AreEqual(@"""first""", json);

json = JsonSerializer.Serialize(EnumDefinition.Second, options);
Assert.AreEqual(@"""_second""", json);
}

[TestMethod]
public void EnumMemberDeserializationOptionsTest()
{
JsonSerializerOptions options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumMemberConverter(JsonNamingPolicy.CamelCase) }
};

EnumDefinition Value = JsonSerializer.Deserialize<EnumDefinition>(@"""first""", options);
Assert.AreEqual(EnumDefinition.First, Value);

Value = JsonSerializer.Deserialize<EnumDefinition>(@"""_second""", options);
Assert.AreEqual(EnumDefinition.Second, Value);
}

[ExpectedException(typeof(JsonException))]
[TestMethod]
public void EnumMemberInvalidDeserializationOptionsTest()
{
JsonSerializerOptions options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumMemberConverter() }
};

JsonSerializer.Deserialize<EnumDefinition>(@"""invalid_value""", options);
}

[ExpectedException(typeof(JsonException))]
[TestMethod]
public void EnumMemberInvalidTypeDeserializationOptionsTest()
{
JsonSerializerOptions options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumMemberConverter(allowIntegerValues: false) }
};

JsonSerializer.Deserialize<EnumDefinition>(@"255", options);
}

[TestMethod]
public void EnumMemberInvalidDeserializationIncludesJsonPathInMessageTest()
{
JsonSerializerOptions options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumMemberConverter() }
};

try
{
JsonSerializer.Deserialize<EnumDefinition>(@"""invalid_value""", options);
Assert.Fail($"A {nameof(JsonException)} is expected to be thrown.");
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception exception)
#pragma warning restore CA1031 // Do not catch general exception types
{
if (exception is JsonException jsonException)
{
StringAssert.Contains(jsonException.Message, ". Path: $");
}
else
{
Assert.Fail($"A {nameof(JsonException)} is expected to be thrown but a {exception.GetType().FullName} was thrown.");
}
}
}

[TestMethod]
public void EnumMemberFlagInvalidDeserializationIncludesJsonPathInMessageTest()
{
JsonSerializerOptions options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumMemberConverter() }
};

try
{
JsonSerializer.Deserialize<FlagDefinitions>(@"""invalid_value""", options);
Assert.Fail($"A {nameof(JsonException)} is expected to be thrown.");
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception exception)
#pragma warning restore CA1031 // Do not catch general exception types
{
if (exception is JsonException jsonException)
{
StringAssert.Contains(jsonException.Message, ". Path: $");
}
else
{
Assert.Fail($"A {nameof(JsonException)} is expected to be thrown but a {exception.GetType().FullName} was thrown.");
}
}
}

[JsonConverter(typeof(JsonStringEnumMemberConverter))]
[Flags]
public enum FlagDefinitions
{
None = 0x00,

[JsonPropertyName("all values")]
All = One | Two | Three | Four,

[JsonPropertyName("one value")]
One = 0x01,
[JsonPropertyName("two value")]
Two = 0x02,
[JsonPropertyName("three value")]
Three = 0x04,
[JsonPropertyName("four value")]
Four = 0x08,
}

public enum EnumDefinition
{
First,

[JsonPropertyName("_second")]
Second,
}
}
}

#endif