The easiest way to start using Extensions.Dictionary
is to install it’s Nuget package. In Visual Studio open Package Manager console and type the following:
Install-Package Extensions.Dictionary
In the source file where you will be using Dictionary Extensions import the namespace:
using Extensions.Dictionary;
Sample class:
public class Person
{
[DataMember(Name = "Name1")]
public string Firstname { get; set; }
[IgnoreDataMember]
public string Lastname { get; set; }
}
Convert an instance to a dictionary:
var person = new Person { Firstname = "foo", Lastname = "bar" };
// Option 1: Convert all public properties and instance fields
var dictionary1 = person.ToDictionary();
// Option 2: Same as option 1 + respect DataContract attributes (DataMember / IgnoreDataMember)
var settings = new ConverterSettings { Resolver = new DataContractResolver() };
var dictionary2 = person.ToDictionary(settings);
// dictionary1: <Firstname = foo; Lastname = bar>
// dictionary2: <Name1 = foo>
Convert a dictionary back to it's typed instance:
// Option 1
var person1 = dictionary.ToInstance<Person>();
// Option 2
var settings = new ConverterSettings { Resolver = new DataContractResolver() };
var person2 = dictionary.ToInstance<Person>(settings);
This sample creates a custom converter from MemberConverter<T>
that overrides conversion for the System.Numerics.Vector4
class.
public class Vector4Converter : MemberConverter<Vector4>
{
public override IDictionary<string, object> ToDictionary(Vector4 value, ConverterSettings settings)
{
return new Dictionary<string, object>(4)
{
{ nameof(Vector4.W), value.W },
{ nameof(Vector4.X), value.X },
{ nameof(Vector4.Y), value.Y },
{ nameof(Vector4.Z), value.Z },
};
}
public override Vector4 ToInstance(IDictionary<string, object?> value, ConverterSettings settings)
{
return new Vector4
{
W = float.Parse(value[nameof(Vector4.W)]?.ToString(), settings.Culture),
X = float.Parse(value[nameof(Vector4.X)]?.ToString(), settings.Culture),
Y = float.Parse(value[nameof(Vector4.Y)]?.ToString(), settings.Culture),
Z = float.Parse(value[nameof(Vector4.Z)]?.ToString(), settings.Culture),
};
}
}
// Usage
var plane = new Plane(1f, 1f, 1f, .1f);
var settings = new ConverterSettings();
settings.Converters.Add(new Vector4Converter());
var dict = plane.ToDictionary(settings);
It is possible to customize the member name / value extraction. Just implement the provided ISerializerResolver
interface. The following code snipped shows an example of a Json.NET resolver:
public class JsonNetResolver : ISerializerResolver
{
public string GetMemberName(MemberInfo memberInfo)
{
if (memberInfo == null)
{
throw new ArgumentNullException(nameof(memberInfo));
}
var attribute = memberInfo.GetCustomAttribute<JsonPropertyAttribute>();
return attribute?.PropertyName ?? memberInfo.Name;
}
public object GetMemberValue(MemberInfo memberInfo, object instance)
{
switch (memberInfo?.MemberType)
{
case MemberTypes.Property:
return ((PropertyInfo)memberInfo).GetValue(instance);
case MemberTypes.Field:
return ((FieldInfo)memberInfo).GetValue(instance);
case null:
throw new ArgumentNullException(nameof(memberInfo));
default:
throw new NotSupportedException(memberInfo.MemberType + " not supported.");
}
}
public MemberInfo[] GetMemberInfos(Type type)
{
return type?
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Cast<MemberInfo>()
.Concat(type.GetFields(BindingFlags.Instance | BindingFlags.Public))
.Where(x => !x.GetCustomAttributes<JsonIgnoreAttribute>().Any())
?? Array.Empty<Type>();
}
}
The following benchmarks are showing the differences between converting from dictionary to instance and from instance to dictionary. SPOILER: You get the best efficiency with the DefaultResolver
.
Test environment
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 8 physical cores
.NET Core SDK=3.0.100
[Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT
ShortRun : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT
Method | N | Mean | Error | StdDev | Ratio | RatioSD | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|---|
DefaultResolver | 1 | 6.277 us | 12.242 us | 0.6710 us | 1.00 | 0.00 | 1 | 0.5875 | - | - | 1.81 KB |
DataContractResolver | 1 | 16.106 us | 18.827 us | 1.0320 us | 2.59 | 0.35 | 3 | 0.9918 | - | - | 3.07 KB |
DataContractResolver-IgnoreAncestors | 1 | 12.773 us | 8.038 us | 0.4406 us | 2.05 | 0.15 | 2 | 0.9766 | - | - | 3.07 KB |
JsonNetResolver | 1 | 50.861 us | 56.199 us | 3.0805 us | 8.14 | 0.56 | 4 | 5.8594 | - | - | 18 KB |
DefaultResolver | 10 | 5.572 us | 1.225 us | 0.0672 us | 1.00 | 0.00 | 1 | 0.5875 | - | - | 1.81 KB |
DataContractResolver | 10 | 14.726 us | 33.944 us | 1.8606 us | 2.64 | 0.32 | 3 | 0.9918 | - | - | 3.07 KB |
DataContractResolver-IgnoreAncestors | 10 | 12.287 us | 1.493 us | 0.0818 us | 2.21 | 0.03 | 2 | 0.9918 | - | - | 3.07 KB |
JsonNetResolver | 10 | 52.918 us | 89.842 us | 4.9246 us | 9.50 | 1.00 | 4 | 5.8594 | - | - | 18 KB |
DefaultResolver | 100 | 6.063 us | 4.733 us | 0.2595 us | 1.00 | 0.00 | 1 | 0.5875 | - | - | 1.81 KB |
DataContractResolver | 100 | 12.419 us | 4.004 us | 0.2195 us | 2.05 | 0.06 | 2 | 0.9918 | - | - | 3.07 KB |
DataContractResolver-IgnoreAncestors | 100 | 12.413 us | 13.096 us | 0.7179 us | 2.05 | 0.20 | 2 | 0.9918 | - | - | 3.07 KB |
JsonNetResolver | 100 | 54.870 us | 36.022 us | 1.9745 us | 9.06 | 0.50 | 3 | 5.8594 | - | - | 18 KB |
Method | N | Mean | Error | StdDev | Ratio | RatioSD | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|---|
DefaultResolver | 1 | 10.76 us | 0.491 us | 0.027 us | 1.00 | 0.00 | 1 | 0.7019 | - | - | 2.18 KB |
DataContractResolver | 1 | 53.33 us | 61.631 us | 3.378 us | 4.96 | 0.32 | 3 | 3.7842 | - | - | 11.72 KB |
DataContractResolver-IgnoreAncestors | 1 | 54.74 us | 99.851 us | 5.473 us | 5.09 | 0.51 | 4 | 3.7842 | - | - | 11.72 KB |
JsonNetResolver | 1 | 161.86 us | 94.736 us | 5.193 us | 15.05 | 0.46 | 5 | 16.6016 | - | - | 51.52 KB |
PureJsonNet | 1 | 18.55 us | 24.079 us | 1.320 us | 1.72 | 0.12 | 2 | 2.4414 | - | - | 7.49 KB |
DefaultResolver | 10 | 11.64 us | 18.704 us | 1.025 us | 1.00 | 0.00 | 1 | 0.7019 | - | - | 2.18 KB |
DataContractResolver | 10 | 64.32 us | 118.677 us | 6.505 us | 5.53 | 0.42 | 3 | 3.7842 | - | - | 11.72 KB |
DataContractResolver-IgnoreAncestors | 10 | 54.44 us | 22.665 us | 1.242 us | 4.70 | 0.40 | 2 | 3.7842 | - | - | 11.72 KB |
JsonNetResolver | 10 | 169.81 us | 208.025 us | 11.403 us | 14.66 | 1.68 | 5 | 16.6016 | - | - | 51.52 KB |
PureJsonNet | 10 | 163.40 us | 185.507 us | 10.168 us | 14.10 | 1.48 | 4 | 11.2305 | - | - | 35.09 KB |
DefaultResolver | 100 | 13.51 us | 13.977 us | 0.766 us | 1.00 | 0.00 | 1 | 0.7019 | - | - | 2.18 KB |
DataContractResolver | 100 | 64.19 us | 64.332 us | 3.526 us | 4.76 | 0.36 | 3 | 3.7842 | - | - | 11.72 KB |
DataContractResolver-IgnoreAncestors | 100 | 56.80 us | 92.891 us | 5.092 us | 4.20 | 0.28 | 2 | 3.7842 | - | - | 11.72 KB |
JsonNetResolver | 100 | 138.36 us | 34.188 us | 1.874 us | 10.26 | 0.44 | 4 | 16.6016 | - | - | 51.52 KB |
PureJsonNet | 100 | 1,858.79 us | 6,022.705 us | 330.125 us | 137.79 | 24.91 | 5 | 93.7500 | - | - | 292.02 KB |
- Icon made by Freepik