Skip to content

Commit

Permalink
Issue #728 - JSON string-to-enum converters improvements:
Browse files Browse the repository at this point in the history
- Added internal helper class for creating/storing string name (from attribute) to value mapping for enums.
- Added internal helper extension method for converting a string into an enum value with fallback handling.
- The new helper method is used for all custom JSON-to-enum converters now.

(cherry picked from commit 00d8a6eefe6045997df8e199b9ba46770bf875f4)
  • Loading branch information
OlegRa committed Feb 28, 2024
1 parent a63bdf1 commit 28e2ed1
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 9 deletions.
4 changes: 2 additions & 2 deletions Alpaca.Markets/Helpers/AssetAttributesEnumConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
Justification = "Object instances of this class will be created by Newtonsoft.JSON library.")]
internal sealed class AssetAttributesEnumConverter : StringEnumConverter
{
public override Object? ReadJson(
public override Object ReadJson(
JsonReader reader,
Type objectType,
Object? existingValue,
JsonSerializer serializer)
{
try
{
return base.ReadJson(reader, objectType, existingValue, serializer);
return AssetAttributes.Unknown.FromEnumString(reader);
}
catch (JsonSerializationException)
{
Expand Down
4 changes: 2 additions & 2 deletions Alpaca.Markets/Helpers/CryptoExchangeEnumConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
Justification = "Object instances of this class will be created by Newtonsoft.JSON library.")]
internal sealed class CryptoExchangeEnumConverter : StringEnumConverter
{
public override Object? ReadJson(
public override Object ReadJson(
JsonReader reader,
Type objectType,
Object? existingValue,
JsonSerializer serializer)
{
try
{
return base.ReadJson(reader, objectType, existingValue, serializer);
return CryptoExchange.Unknown.FromEnumString(reader);
}
catch (JsonSerializationException)
{
Expand Down
7 changes: 7 additions & 0 deletions Alpaca.Markets/Helpers/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@ public static IReadOnlyDictionary<String, IReadOnlyList<TInto>> SetSymbol<TInto,
pair => pair.Value.SetSymbol(pair.Key).EmptyIfNull<TInto, TFrom>(),
StringComparer.Ordinal)
?? new Dictionary<String, IReadOnlyList<TInto>>(StringComparer.Ordinal);

#if NETFRAMEWORK || NETSTANDARD2_0
public static TValue GetValueOrDefault<TKey, TValue>(
this IReadOnlyDictionary<TKey, TValue> dictionary,
TKey key, TValue defaultValue) =>
dictionary.TryGetValue(key, out var value) ? value : defaultValue;
#endif
}
33 changes: 32 additions & 1 deletion Alpaca.Markets/Helpers/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,42 @@
namespace Alpaca.Markets;
using System.Reflection;

namespace Alpaca.Markets;

internal static class EnumExtensions
{
private static class NamesHelper<
#if NET6_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
#endif
T> where T : struct, Enum
{
public static readonly IReadOnlyDictionary<String, T> ValuesByNames =
Enum.GetValues(typeof(T)).OfType<T>()
.ToDictionary(getJsonName, value => value);

private static string getJsonName(
T value) =>
typeof(T).GetField(Enum.GetName(typeof(T), value) ?? value.ToString())?
.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? value.ToString();
}

private static readonly Char[] _doubleQuotes = [ '"' ];

public static String ToEnumString<T>(
this T enumValue)
where T : struct, Enum =>
JsonConvert.SerializeObject(enumValue).Trim(_doubleQuotes);

public static T FromEnumString<
#if NET6_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
#endif
T>(
this T fallbackEnumValue,
JsonReader reader)
where T : struct, Enum =>
reader.TokenType == JsonToken.String
? NamesHelper<T>.ValuesByNames.GetValueOrDefault(
reader.Value as String ?? String.Empty, fallbackEnumValue)
: fallbackEnumValue;
}
4 changes: 2 additions & 2 deletions Alpaca.Markets/Helpers/ExchangeEnumConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
Justification = "Object instances of this class will be created by Newtonsoft.JSON library.")]
internal sealed class ExchangeEnumConverter : StringEnumConverter
{
public override Object? ReadJson(
public override Object ReadJson(
JsonReader reader,
Type objectType,
Object? existingValue,
JsonSerializer serializer)
{
try
{
return base.ReadJson(reader, objectType, existingValue, serializer);
return Exchange.Unknown.FromEnumString(reader);
}
catch (JsonSerializationException)
{
Expand Down
4 changes: 2 additions & 2 deletions Alpaca.Markets/Helpers/OrderSideEnumConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
Justification = "Object instances of this class will be created by Newtonsoft.JSON library.")]
internal sealed class OrderSideEnumConverter : StringEnumConverter
{
public override Object? ReadJson(
public override Object ReadJson(
JsonReader reader,
Type objectType,
Object? existingValue,
JsonSerializer serializer)
{
try
{
return base.ReadJson(reader, objectType, existingValue, serializer);
return OrderSide.Sell.FromEnumString(reader);
}
catch (JsonSerializationException)
{
Expand Down

0 comments on commit 28e2ed1

Please sign in to comment.