diff --git a/Directory.Packages.props b/Directory.Packages.props index e51e66d..ee124af 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,6 @@ - @@ -12,6 +11,7 @@ + diff --git a/src/FX.Core/FX.Core.fsproj b/src/FX.Core/FX.Core.fsproj index b1da1f4..65d63ec 100644 --- a/src/FX.Core/FX.Core.fsproj +++ b/src/FX.Core/FX.Core.fsproj @@ -13,6 +13,6 @@ - + diff --git a/src/FX.Core/RedisStorageLayer.fs b/src/FX.Core/RedisStorageLayer.fs index bedd39e..07a9070 100644 --- a/src/FX.Core/RedisStorageLayer.fs +++ b/src/FX.Core/RedisStorageLayer.fs @@ -5,9 +5,42 @@ open System open FsharpExchangeDotNetStandard -open Newtonsoft.Json +open System.Text.Json +open System.Text.Json.Serialization open StackExchange.Redis +[] +module Serialization = + // TODO: use FSharp.SystemTextJson for Discriminated Union support, + // in that case custom type converters are no longer needed + type SideTypeConverter() = + inherit JsonConverter() + + override this.Read(reader, _typeToConvert, _options) = + reader.GetString() |> Side.Parse + + override this.Write(writer, value, _options ) = + writer.WriteStringValue(value.ToString()) + + type CurrencyTypeConverter() = + inherit JsonConverter() + + override this.Read(reader, _typeToConvert, _options) = + match reader.GetString() with + | "BTC" -> BTC + | "USD" -> USD + | unknownCurrency -> failwithf "Unknown currency: %s" unknownCurrency + + override this.Write(writer, value, _options ) = + writer.WriteStringValue(value.ToString()) + + let serializationOptions = + let options = JsonSerializerOptions() + options.Converters.Add(SideTypeConverter()) + options.Converters.Add(CurrencyTypeConverter()) + options + + type OrderQuery = { OrderId: string; @@ -26,9 +59,9 @@ type Query = type OrderBookSide(market: Market, side: Side) = let tipQuery = { Market = market; Tip = true; Side = side } - let tipQueryStr = JsonConvert.SerializeObject tipQuery + let tipQueryStr = JsonSerializer.Serialize(tipQuery, serializationOptions) let tailQuery = { Market = market; Tip = false; Side = side } - let tailQueryStr = JsonConvert.SerializeObject tailQuery + let tailQueryStr = JsonSerializer.Serialize(tailQuery, serializationOptions) member __.TipQuery = tipQueryStr member __.TailQuery = tailQueryStr @@ -53,7 +86,7 @@ module OrderRedisManager = db.CreateTransaction() let InsertOrder (limitOrder: LimitOrder): unit = - let serializedOrder = JsonConvert.SerializeObject limitOrder + let serializedOrder = JsonSerializer.Serialize(limitOrder, serializationOptions) let success = db.StringSet(RedisKey.op_Implicit (limitOrder.OrderInfo.Id.ToString()), RedisValue.op_Implicit (serializedOrder)) if not success then @@ -89,7 +122,7 @@ module OrderRedisManager = let orderSerialized = db.StringGet (RedisKey.op_Implicit tipOrderGuid) if not orderSerialized.HasValue then failwithf "Something went wrong, order tip was %s but was not found" tipOrderGuid - let tipOrder = JsonConvert.DeserializeObject (orderSerialized.ToString()) + let tipOrder = JsonSerializer.Deserialize(orderSerialized.ToString(), serializationOptions) tipOrder |> Some let private GetOrderSerialized (guidStr: string): Option = @@ -111,7 +144,7 @@ module OrderRedisManager = match maybeOrderSerialized with | None -> None | Some orderSerialized -> - let order = JsonConvert.DeserializeObject (orderSerialized.ToString()) + let order = JsonSerializer.Deserialize(orderSerialized.ToString(), serializationOptions) order |> Some let GetOrderByGuid (guid: Guid): Option = @@ -122,13 +155,13 @@ module OrderRedisManager = if not tail.HasValue then List.empty else - JsonConvert.DeserializeObject> (tail.ToString()) + JsonSerializer.Deserialize>(tail.ToString(), serializationOptions) let SetTail (transaction: StackExchange.Redis.ITransaction) (limitOrderGuids: List) (orderBookSide: OrderBookSide) : unit = - let serializedGuids = JsonConvert.SerializeObject limitOrderGuids + let serializedGuids = JsonSerializer.Serialize(limitOrderGuids, serializationOptions) transaction.StringSetAsync(RedisKey.op_Implicit orderBookSide.TailQuery, RedisValue.op_Implicit serializedGuids) |> ignore diff --git a/src/FX.Tests/FX.Tests.csproj b/src/FX.Tests/FX.Tests.csproj index 7bef3f5..0a2ee99 100644 --- a/src/FX.Tests/FX.Tests.csproj +++ b/src/FX.Tests/FX.Tests.csproj @@ -9,8 +9,8 @@ - + diff --git a/src/FX.Tests/RedisIntegrationTests.cs b/src/FX.Tests/RedisIntegrationTests.cs index 19697e6..a8df019 100644 --- a/src/FX.Tests/RedisIntegrationTests.cs +++ b/src/FX.Tests/RedisIntegrationTests.cs @@ -5,7 +5,7 @@ using FsharpExchangeDotNetStandard; using FsharpExchangeDotNetStandard.Redis; -using Newtonsoft.Json; +using System.Text.Json; using NUnit.Framework; using StackExchange.Redis; @@ -33,8 +33,8 @@ private Exchange CreateExchangeAndSendFirstLimitOrder var tipQuery = new MarketQuery(market, limitOrder.OrderInfo.Side, true); - //e.g. {"Market":{"BuyCurrency":{"Case":"BTC"},"SellCurrency":{"Case":"USD"}},"Side":{"Case":"Buy"},"Tip":true}" - string tipQueryStr = JsonConvert.SerializeObject(tipQuery); + //e.g. {"Market":{"BuyCurrency":"BTC","SellCurrency":"USD"},"Side":"Bid","Tip":true}" + string tipQueryStr = JsonSerializer.Serialize(tipQuery, Serialization.serializationOptions); using (var redis = ConnectionMultiplexer.Connect("localhost")) { @@ -76,7 +76,7 @@ private Exchange CreateExchangeAndSendFirstLimitOrder "should have the order content(not null)"); var limitOrderSerialized = - JsonConvert.SerializeObject(limitOrder); + JsonSerializer.Serialize(limitOrder, Serialization.serializationOptions); Assert.That(order.ToString(), Is.EqualTo(limitOrderSerialized), "received order should have same content"); @@ -129,8 +129,8 @@ public void SendingSecondAndThirdLimitOrderMakesNonTipQueryWorkAfter() var nonTipQuery = new MarketQuery(market, side, false); - //e.g. {"Market":{"BuyCurrency":{"Case":"BTC"},"SellCurrency":{"Case":"USD"}},"Side":{"Case":"Buy"},"Tip":true}" - string nontipQueryStr = JsonConvert.SerializeObject(nonTipQuery); + //e.g. {"Market":{"BuyCurrency":"BTC","SellCurrency":"USD"},"Side":"Bid","Tip":false}" + string nontipQueryStr = JsonSerializer.Serialize(nonTipQuery, Serialization.serializationOptions); using (var redis = ConnectionMultiplexer.Connect("localhost")) { @@ -139,7 +139,7 @@ public void SendingSecondAndThirdLimitOrderMakesNonTipQueryWorkAfter() var values = db.StringGet(nontipQueryStr); Assert.That(String.IsNullOrEmpty(values), Is.False, "should have nontip tail(not null) in this market"); - var orders = JsonConvert.DeserializeObject>(values); + var orders = JsonSerializer.Deserialize>(values, Serialization.serializationOptions); Assert.That(orders.Count, Is.EqualTo(2), "should have nontip tail of 2 elements in this market now"); @@ -156,7 +156,7 @@ public void SendingSecondAndThirdLimitOrderMakesNonTipQueryWorkAfter() Assert.That(order2.IsNull, Is.EqualTo(false), "should have the second order content(not null)"); var secondLimitOrderSerialized = - JsonConvert.SerializeObject(secondLimitOrder); + JsonSerializer.Serialize(secondLimitOrder, Serialization.serializationOptions); Assert.That(order2.ToString(), Is.EqualTo(secondLimitOrderSerialized), "received second order should have same content"); @@ -167,7 +167,7 @@ public void SendingSecondAndThirdLimitOrderMakesNonTipQueryWorkAfter() Assert.That(order3.IsNull, Is.EqualTo(false), "should have the third order content(not null)"); var thirdLimitOrderSerialized = - JsonConvert.SerializeObject(thirdLimitOrder); + JsonSerializer.Serialize(thirdLimitOrder, Serialization.serializationOptions); Assert.That(order3.ToString(), Is.EqualTo(thirdLimitOrderSerialized), "received second order should have same content"); @@ -198,8 +198,8 @@ public void TipIsReplaced() var nonTipQuery = new MarketQuery(market, side, false); - //e.g. {"Market":{"BuyCurrency":{"Case":"BTC"},"SellCurrency":{"Case":"USD"}},"Side":{"Case":"Buy"},"Tip":true}" - string nontipQueryStr = JsonConvert.SerializeObject(nonTipQuery); + //e.g. {"Market":{"BuyCurrency":"BTC","SellCurrency":"USD"},"Side":"Bid","Tip":false}" + string nontipQueryStr = JsonSerializer.Serialize(nonTipQuery, Serialization.serializationOptions); using (var redis = ConnectionMultiplexer.Connect("localhost")) { @@ -208,7 +208,7 @@ public void TipIsReplaced() var values = db.StringGet(nontipQueryStr); Assert.That(String.IsNullOrEmpty(values), Is.False, "should have nontip tail(not null) in this market"); - var orders = JsonConvert.DeserializeObject>(values); + var orders = JsonSerializer.Deserialize>(values, Serialization.serializationOptions); Assert.That(orders.Count, Is.EqualTo(1), "should have nontip tail of 2 elements in this market now"); @@ -222,7 +222,7 @@ public void TipIsReplaced() Assert.That(theOrder.IsNull, Is.EqualTo(false), "should have the second order content(not null)"); var firstLimitOrderSerialized = - JsonConvert.SerializeObject(firstLimitOrder); + JsonSerializer.Serialize(firstLimitOrder, Serialization.serializationOptions); Assert.That(theOrder.ToString(), Is.EqualTo(firstLimitOrderSerialized), "received second order should have same content");