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:
parent
78f81393a4
commit
9a266e44ce
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
15
CryptoExchange.Net/Attributes/MapAttribute.cs
Normal file
15
CryptoExchange.Net/Attributes/MapAttribute.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
CryptoExchange.Net/Converters/EnumConverter.cs
Normal file
101
CryptoExchange.Net/Converters/EnumConverter.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user