1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-07-26 19:26:47 +00:00

Added enum converter

This commit is contained in:
Jkorf 2021-11-26 09:32:26 +01:00
parent 78f81393a4
commit 9a266e44ce
3 changed files with 173 additions and 1 deletions

View File

@ -1,4 +1,5 @@
using CryptoExchange.Net.Converters; using CryptoExchange.Net.Attributes;
using CryptoExchange.Net.Converters;
using Newtonsoft.Json; using Newtonsoft.Json;
using NUnit.Framework; using NUnit.Framework;
using System; using System;
@ -51,6 +52,44 @@ namespace CryptoExchange.Net.UnitTests
var output = JsonConvert.DeserializeObject<TimeObject>($"{{ \"time\": null }}"); var output = JsonConvert.DeserializeObject<TimeObject>($"{{ \"time\": null }}");
Assert.AreEqual(output.Time, null); Assert.AreEqual(output.Time, null);
} }
// TODO add tests for ToMilliseconds static methods
[TestCase(TestEnum.One, "1")]
[TestCase(TestEnum.Two, "2")]
[TestCase(TestEnum.Three, "three")]
[TestCase(TestEnum.Four, "Four")]
[TestCase(null, null)]
public void TestEnumConverterNullableGetStringTests(TestEnum? value, string expected)
{
var output = EnumConverter.GetString(value);
Assert.AreEqual(output, expected);
}
[TestCase(TestEnum.One, "1")]
[TestCase(TestEnum.Two, "2")]
[TestCase(TestEnum.Three, "three")]
[TestCase(TestEnum.Four, "Four")]
public void TestEnumConverterGetStringTests(TestEnum value, string expected)
{
var output = EnumConverter.GetString(value);
Assert.AreEqual(output, expected);
}
[TestCase("1", TestEnum.One)]
[TestCase("2", TestEnum.Two)]
[TestCase("3", TestEnum.Three)]
[TestCase("three", TestEnum.Three)]
[TestCase("Four", TestEnum.Four)]
[TestCase("four", TestEnum.Four)]
[TestCase("Four1", null)]
[TestCase(null, null)]
public void TestEnumConverterNullableDeserializeTests(string? value, TestEnum? expected)
{
var val = value == null ? "null" : $"\"{value}\"";
var output = JsonConvert.DeserializeObject<EnumObject?>($"{{ \"Value\": {val} }}");
Assert.AreEqual(output.Value, expected);
}
} }
public class TimeObject public class TimeObject
@ -58,4 +97,21 @@ namespace CryptoExchange.Net.UnitTests
[JsonConverter(typeof(DateTimeConverter))] [JsonConverter(typeof(DateTimeConverter))]
public DateTime? Time { get; set; } public DateTime? Time { get; set; }
} }
public class EnumObject
{
public TestEnum? Value { get; set; }
}
[JsonConverter(typeof(EnumConverter))]
public enum TestEnum
{
[Map("1")]
One,
[Map("2")]
Two,
[Map("three", "3")]
Three,
Four
}
} }

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.Attributes
{
public class MapAttribute : Attribute
{
public string[] Values { get; set; }
public MapAttribute(params string[] maps)
{
Values = maps;
}
}
}

View File

@ -0,0 +1,101 @@
using CryptoExchange.Net.Attributes;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace CryptoExchange.Net.Converters
{
public class EnumConverter : JsonConverter
{
private static ConcurrentDictionary<Type, List<KeyValuePair<object, string>>> _mapping = new ConcurrentDictionary<Type, List<KeyValuePair<object, string>>>();
public override bool CanConvert(Type objectType)
{
return objectType.IsEnum;
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
objectType = Nullable.GetUnderlyingType(objectType) ?? objectType;
if (!_mapping.TryGetValue(objectType, out var mapping))
mapping = AddMapping(objectType);
if (reader.Value == null)
return null;
var stringValue = reader.Value.ToString();
if (string.IsNullOrWhiteSpace(stringValue))
return null;
if (!GetValue(objectType, mapping, stringValue, out var result))
{
Debug.WriteLine($"Cannot map enum. Type: {objectType.Name}, Value: {reader.Value}");
return null;
}
return result;
}
private static List<KeyValuePair<object, string>> AddMapping(Type objectType)
{
var mapping = new List<KeyValuePair<object, string>>();
var enumMembers = objectType.GetMembers();
foreach (var member in enumMembers)
{
var maps = member.GetCustomAttributes(typeof(MapAttribute), false);
foreach (MapAttribute attribute in maps)
{
foreach (var value in attribute.Values)
mapping.Add(new KeyValuePair<object, string>(Enum.Parse(objectType, member.Name), value));
}
}
_mapping.TryAdd(objectType, mapping);
return mapping;
}
private bool GetValue(Type objectType, List<KeyValuePair<object, string>> enumMapping, string value, out object? result)
{
// Check for exact match first, then if not found fallback to a case insensitive match
var mapping = enumMapping.FirstOrDefault(kv => kv.Value.Equals(value, StringComparison.InvariantCulture));
if (mapping.Equals(default(KeyValuePair<object, string>)))
mapping = enumMapping.FirstOrDefault(kv => kv.Value.Equals(value, StringComparison.InvariantCultureIgnoreCase));
if (!mapping.Equals(default(KeyValuePair<object, string>)))
{
result = mapping.Key;
return true;
}
try
{
result = Enum.Parse(objectType, value, true);
return true;
}
catch (Exception)
{
result = default;
return false;
}
}
public static string? GetString<T>(T enumValue)
{
var objectType = typeof(T);
objectType = Nullable.GetUnderlyingType(objectType) ?? objectType;
if (!_mapping.TryGetValue(objectType, out var mapping))
mapping = AddMapping(objectType);
return enumValue == null ? null : (mapping.FirstOrDefault(v => v.Key.Equals(enumValue)).Value ?? enumValue.ToString());
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
var stringValue = GetString(value);
writer.WriteRawValue(stringValue);
}
}
}