Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MrBogomips/a72c002291732101941ac52c58575288 to your computer and use it in GitHub Desktop.
Save MrBogomips/a72c002291732101941ac52c58575288 to your computer and use it in GitHub Desktop.
.NET JsonConverterFactory for Dictionary<string, T>.
/*
* JsonConverterFactory to serialize Dictionary with string as keys.
* This serializer will produce a json where the key becomes the property of the json item.
*/
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Bogoware.Common.JsonConverters;
/// <summary>
/// Json converter for <see cref="Dictionary{TKey,TValue}"/> where keys are strings and values are objects.
/// </summary>
public class DictionaryKeyValueJsonConverterFactory: JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType) return false;
if (typeToConvert.GetGenericTypeDefinition() != typeof(Dictionary<,>)) return false;
return typeToConvert.GetGenericArguments()[0] == typeof(string);
}
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
var valueType = typeToConvert.GetGenericArguments()[1];
var converterType = typeof(DictionaryKeyValueJsonConverter<>).MakeGenericType(valueType);
return (JsonConverter?)Activator.CreateInstance(converterType);
}
private class DictionaryKeyValueJsonConverter<TValue>: JsonConverter<Dictionary<string, TValue>>
{
public override Dictionary<string, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var dictionary = new Dictionary<string, TValue>();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject) break;
if (reader.TokenType != JsonTokenType.PropertyName) throw new JsonException("Expected property name.");
var key = reader.GetString();
reader.Read();
var value = JsonSerializer.Deserialize<TValue>(ref reader, options);
dictionary.Add(key, value);
}
return dictionary;
}
public override void Write(Utf8JsonWriter writer, Dictionary<string, TValue> dictionary, JsonSerializerOptions options)
{
writer.WriteStartObject();
foreach (var (key, value) in dictionary)
{
writer.WritePropertyName(key);
JsonSerializer.Serialize(writer, value, options);
}
writer.WriteEndObject();
}
}
}
/************** Test Class base on XUnit/FluentAsserion **********************/
using System.Text.Json;
using FluentAssertions;
using Bogoware.Common.JsonConverters;
using Xunit.Abstractions;
namespace Bogoware.Common.Test.Unit;
public class DictionaryKeyValueJsonConverterFactoryTests
{
private readonly ITestOutputHelper _output;
private class Person
{
public string Name { get; set; } = null!;
public int Age { get; set; }
public bool LikesChocolate { get; set; }
}
public DictionaryKeyValueJsonConverterFactoryTests(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void Sut_CanSerialize_DictionaryOfStringString()
{
// Arrange
var jsonOptions = new JsonSerializerOptions()
{
Converters = { new DictionaryKeyValueJsonConverterFactory() }
};
var dictionary = new Dictionary<string, string>();
dictionary.Add("key1", "value1");
dictionary.Add("key2", "value2");
dictionary.Add("key3", "value3");
// Act
var json = JsonSerializer.Serialize(dictionary, jsonOptions);
// Assert
_output.WriteLine(json);
json.Should().Be("{\"key1\":\"value1\",\"key2\":\"value2\",\"key3\":\"value3\"}");
}
[Fact]
public void Sut_CanSerialize_DictionaryOfStringDecimal()
{
// Arrange
var jsonOptions = new JsonSerializerOptions()
{
Converters = { new DictionaryKeyValueJsonConverterFactory() }
};
var dictionary = new Dictionary<string, decimal>();
dictionary.Add("key1", 1m);
dictionary.Add("key2", 2m);
dictionary.Add("key3", 3m);
// Act
var json = JsonSerializer.Serialize(dictionary, jsonOptions);
// Assert
_output.WriteLine(json);
json.Should().Be("{\"key1\":1,\"key2\":2,\"key3\":3}");
}
[Fact]
public void Sut_CanSerialize_DictionaryOfStringPerson()
{
// Arrange
var jsonOptions = new JsonSerializerOptions()
{
Converters = { new DictionaryKeyValueJsonConverterFactory() },
};
var dictionary = new Dictionary<string, Person>();
dictionary.Add("John", new Person { Name = "John", Age = 42, LikesChocolate = true });
dictionary.Add("Jane", new Person { Name = "Jane", Age = 39, LikesChocolate = false });
dictionary.Add("Jack", new Person { Name = "Jack", Age = 12, LikesChocolate = true });
// Act
var json = JsonSerializer.Serialize(dictionary, jsonOptions);
var prettyJson = JsonSerializer.Serialize(dictionary, new JsonSerializerOptions { WriteIndented = true });
// Assert
_output.WriteLine(prettyJson);
json.Should().Be("{\"John\":{\"Name\":\"John\",\"Age\":42,\"LikesChocolate\":true},\"Jane\":{\"Name\":\"Jane\",\"Age\":39,\"LikesChocolate\":false},\"Jack\":{\"Name\":\"Jack\",\"Age\":12,\"LikesChocolate\":true}}");
}
[Fact]
public void Sut_CanDeserialize_DictionaryOfStringString()
{
// Arrange
var jsonOptions = new JsonSerializerOptions()
{
Converters = { new DictionaryKeyValueJsonConverterFactory() }
};
var json = "{\"key1\":\"value1\",\"key2\":\"value2\",\"key3\":\"value3\"}";
// Act
var dictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(json, jsonOptions);
// Assert
dictionary.Should().NotBeNull();
dictionary!.Count.Should().Be(3);
dictionary["key1"].Should().Be("value1");
dictionary["key2"].Should().Be("value2");
dictionary["key3"].Should().Be("value3");
}
[Fact]
public void Sut_CanDeserialize_DictionaryOfStringDecimal()
{
// Arrange
var jsonOptions = new JsonSerializerOptions()
{
Converters = { new DictionaryKeyValueJsonConverterFactory() }
};
var json = "{\"key1\":1,\"key2\":2,\"key3\":3}";
// Act
var dictionary = JsonSerializer.Deserialize<Dictionary<string, decimal>>(json, jsonOptions);
// Assert
dictionary.Should().NotBeNull();
dictionary!.Count.Should().Be(3);
dictionary["key1"].Should().Be(1m);
dictionary["key2"].Should().Be(2m);
dictionary["key3"].Should().Be(3m);
}
[Fact]
public void Sut_CanDeserialize_DictionaryOfStringPerson()
{
// Arrange
var jsonOptions = new JsonSerializerOptions()
{
Converters = { new DictionaryKeyValueJsonConverterFactory() },
};
var json = "{\"John\":{\"Name\":\"John\",\"Age\":42,\"LikesChocolate\":true},\"Jane\":{\"Name\":\"Jane\",\"Age\":39,\"LikesChocolate\":false},\"Jack\":{\"Name\":\"Jack\",\"Age\":12,\"LikesChocolate\":true}}";
// Act
var dictionary = JsonSerializer.Deserialize<Dictionary<string, Person>>(json, jsonOptions);
// Assert
dictionary.Should().NotBeNull();
dictionary!.Count.Should().Be(3);
dictionary["John"].Name.Should().Be("John");
dictionary["John"].Age.Should().Be(42);
dictionary["John"].LikesChocolate.Should().BeTrue();
dictionary["Jane"].Name.Should().Be("Jane");
dictionary["Jane"].Age.Should().Be(39);
dictionary["Jane"].LikesChocolate.Should().BeFalse();
dictionary["Jack"].Name.Should().Be("Jack");
dictionary["Jack"].Age.Should().Be(12);
dictionary["Jack"].LikesChocolate.Should().BeTrue();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment