Skip to content

Commit c4b6a44

Browse files
authored
Merge pull request #29 from VelvetToroyashi/velvet/feat/dictionary-enum-keys
Add support for enum dictionary keys
2 parents b4ab622 + aec822e commit c4b6a44

File tree

2 files changed

+174
-1
lines changed

2 files changed

+174
-1
lines changed

Remora.Rest/Json/StringEnumConverter.cs

+20-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe
5959

6060
switch (reader.TokenType)
6161
{
62-
case JsonTokenType.String:
62+
case JsonTokenType.String or JsonTokenType.PropertyName:
6363
{
6464
var value = reader.GetString();
6565
if (value is null)
@@ -117,4 +117,23 @@ public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOpt
117117

118118
writer.WriteStringValue(_enumsToNames[value]);
119119
}
120+
121+
/// <inheritdoc />
122+
public override TEnum ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
123+
=> this.Read(ref reader, typeToConvert, options);
124+
125+
/// <inheritdoc/>
126+
public override void WriteAsPropertyName(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
127+
{
128+
if (_asInteger)
129+
{
130+
writer.WritePropertyName(Enum.GetUnderlyingType(typeof(TEnum)).IsUnsigned()
131+
? Convert.ToUInt64(value).ToString()
132+
: Convert.ToInt64(value).ToString());
133+
134+
return;
135+
}
136+
137+
writer.WritePropertyName(_enumsToNames[value]);
138+
}
120139
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//
2+
// SPDX-FileName: StringEnumConverterTests.cs
3+
// SPDX-FileCopyrightText: Copyright (c) Jarl Gullberg
4+
// SPDX-License-Identifier: LGPL-3.0-or-later
5+
//
6+
7+
using System.Collections.Generic;
8+
using System.Text.Json;
9+
using Microsoft.Extensions.DependencyInjection;
10+
using Microsoft.Extensions.Options;
11+
using Remora.Rest.Json;
12+
using Remora.Rest.Json.Policies;
13+
using Remora.Rest.Tests.Data.DataObjects;
14+
using Xunit;
15+
16+
namespace Remora.Rest.Tests.Json;
17+
18+
/// <summary>
19+
/// Tests for <see cref="StringEnumConverter{TEnum}"/>.
20+
/// </summary>
21+
public class StringEnumConverterTests
22+
{
23+
/// <summary>
24+
/// Tests that the converter can serialize a dictionary where the key is an enum.
25+
/// </summary>
26+
[Fact]
27+
public void CanSerializeDictionaryKeyAsInteger()
28+
{
29+
// Arrange
30+
var services = new ServiceCollection()
31+
.Configure<JsonSerializerOptions>
32+
(
33+
json =>
34+
{
35+
json.PropertyNamingPolicy = new SnakeCaseNamingPolicy();
36+
json.Converters.Add(new StringEnumConverter<StringifiedEnum>(asInteger: true));
37+
})
38+
.BuildServiceProvider();
39+
40+
var dictionary = new Dictionary<StringifiedEnum, string>
41+
{
42+
{ StringifiedEnum.First, "first" },
43+
{ StringifiedEnum.Second, "second" },
44+
{ StringifiedEnum.Third, "third" },
45+
};
46+
47+
var jsonOptions = services.GetRequiredService<IOptions<JsonSerializerOptions>>().Value;
48+
49+
// Act
50+
var result = JsonSerializer.Serialize(dictionary, jsonOptions);
51+
var expected = "{\"0\":\"first\",\"1\":\"second\",\"2\":\"third\"}";
52+
53+
// Assert
54+
Assert.Equal(expected, result);
55+
}
56+
57+
/// <summary>
58+
/// Tests that the converter can serialize a dictionary where the key is an enum.
59+
/// </summary>
60+
[Fact]
61+
public void CanSerializeDictionaryKeyAsString()
62+
{
63+
// Arrange
64+
var services = new ServiceCollection()
65+
.Configure<JsonSerializerOptions>
66+
(
67+
json =>
68+
{
69+
json.PropertyNamingPolicy = new SnakeCaseNamingPolicy();
70+
json.Converters.Add(new StringEnumConverter<StringifiedEnum>(json.PropertyNamingPolicy));
71+
})
72+
.BuildServiceProvider();
73+
74+
var dictionary = new Dictionary<StringifiedEnum, string>
75+
{
76+
{ StringifiedEnum.First, "first" },
77+
{ StringifiedEnum.Second, "second" },
78+
{ StringifiedEnum.Third, "third" },
79+
};
80+
81+
var jsonOptions = services.GetRequiredService<IOptions<JsonSerializerOptions>>().Value;
82+
83+
// Act
84+
var result = JsonSerializer.Serialize(dictionary, jsonOptions);
85+
var expected = "{\"first\":\"first\",\"second\":\"second\",\"third\":\"third\"}";
86+
87+
// Assert
88+
Assert.Equal(expected, result);
89+
}
90+
91+
/// <summary>
92+
/// Tests that the converter can read a dictionary where the key is an enum.
93+
/// </summary>
94+
[Fact]
95+
public void CanDeserializeDictionaryKeyAsInteger()
96+
{
97+
// Arrange
98+
var services = new ServiceCollection()
99+
.Configure<JsonSerializerOptions>
100+
(
101+
json =>
102+
{
103+
json.PropertyNamingPolicy = new SnakeCaseNamingPolicy();
104+
json.Converters.Add(new StringEnumConverter<StringifiedEnum>(asInteger: true));
105+
})
106+
.BuildServiceProvider();
107+
108+
var jsonOptions = services.GetRequiredService<IOptions<JsonSerializerOptions>>().Value;
109+
110+
var json = "{\"0\":\"first\",\"1\":\"second\",\"2\":\"third\"}";
111+
112+
// Act
113+
var result = JsonSerializer.Deserialize<Dictionary<StringifiedEnum, string>>(json, jsonOptions);
114+
115+
// Assert
116+
Assert.NotNull(result);
117+
Assert.Equal(3, result.Count);
118+
Assert.Equal("first", result[StringifiedEnum.First]);
119+
Assert.Equal("second", result[StringifiedEnum.Second]);
120+
Assert.Equal("third", result[StringifiedEnum.Third]);
121+
}
122+
123+
/// <summary>
124+
/// Tests that the converter can read a dictionary where the key is an enum.
125+
/// </summary>
126+
[Fact]
127+
public void CanDeserializeDictionaryKeyAsString()
128+
{
129+
// Arrange
130+
var services = new ServiceCollection()
131+
.Configure<JsonSerializerOptions>
132+
(
133+
json =>
134+
{
135+
json.PropertyNamingPolicy = new SnakeCaseNamingPolicy();
136+
json.Converters.Add(new StringEnumConverter<StringifiedEnum>(json.PropertyNamingPolicy));
137+
})
138+
.BuildServiceProvider();
139+
140+
var jsonOptions = services.GetRequiredService<IOptions<JsonSerializerOptions>>().Value;
141+
142+
var json = "{\"first\":\"first\",\"second\":\"second\",\"third\":\"third\"}";
143+
144+
// Act
145+
var result = JsonSerializer.Deserialize<Dictionary<StringifiedEnum, string>>(json, jsonOptions);
146+
147+
// Assert
148+
Assert.NotNull(result);
149+
Assert.Equal(3, result.Count);
150+
Assert.Equal("first", result[StringifiedEnum.First]);
151+
Assert.Equal("second", result[StringifiedEnum.Second]);
152+
Assert.Equal("third", result[StringifiedEnum.Third]);
153+
}
154+
}

0 commit comments

Comments
 (0)