nop
nop

Reputation: 6331

System.Text.Json unable to convert a property which is a struct

System.Text.Json.JsonException: The JSON value could not be converted to Models.Side. Path: $.S | LineNumber: 0 | BytePositionInLine: 107.

JSON

{
    "stream": "c3uwBXY4P4eHD2HNkk5Tmj9SzXKZHRfxMpiWrQsXkvICePRWecoN5gFInj6O",
    "data": {
        "e": "executionReport",
        "E": 1656027820175,
        "s": "BTCUSDT",
        "c": "electron_77d6dd561690469e8fdfe178738",
        "S": "BUY",
        "o": "LIMIT",
        "f": "GTC",
        "q": "0.00105000",
        "p": "19000.00000000",
        "P": "0.00000000",
        "F": "0.00000000",
        "g": -1,
        "C": "",
        "x": "NEW",
        "X": "NEW",
        "r": "NONE",
        "i": 11203182805,
        "l": "0.00000000",
        "z": "0.00000000",
        "L": "0.00000000",
        "n": "0",
        "N": null,
        "T": 1656027820174,
        "t": -1,
        "I": 23804088798,
        "w": true,
        "m": false,
        "M": false,
        "O": 1656027820174,
        "Z": "0.00000000",
        "Y": "0.00000000",
        "Q": "0.00000000"
    }
}

Code

public readonly struct Side
{
    private Side(string value)
    {
        Value = value;
    }

    public static Side Buy => new("BUY");
    public static Side Sell => new("SELL");

    public string Value { get; }

    public static implicit operator string(Side enm)
    {
        return enm.Value;
    }

    public override string ToString()
    {
        return Value;
    }
}

public record BinanceStreamOrderUpdate
{
    [JsonPropertyName("i")]
    public long Id { get; init; }

    [JsonPropertyName("s")]
    public string Symbol { get; init; } = null!;

    [JsonPropertyName("c")]
    public string ClientOrderId { get; init; } = null!;

    [JsonPropertyName("S")]
    public Side Side { get; init; }

    [JsonPropertyName("o")]
    public SpotOrderType Type { get; init; }

    [JsonPropertyName("f")]
    public TimeInForce TimeInForce { get; init; }

    [JsonPropertyName("q")]
    public decimal Quantity { get; init; }

    [JsonPropertyName("p")]
    public decimal Price { get; init; }

    [JsonPropertyName("P")]
    public decimal StopPrice { get; init; }

    [JsonPropertyName("d")]
    public int? TrailingDelta { get; init; }

    [JsonPropertyName("F")]
    public decimal IcebergQuantity { get; init; }

    [JsonPropertyName("C")]
    public string? OriginalClientOrderId { get; init; }

    [JsonPropertyName("x")]
    public ExecutionType ExecutionType { get; init; }

    [JsonPropertyName("X")]
    public OrderStatus Status { get; init; }

    [JsonPropertyName("r")]
    public OrderRejectReason RejectReason { get; init; }

    [JsonPropertyName("l")]
    public decimal LastQuantityFilled { get; init; }

    [JsonPropertyName("z")]
    public decimal QuantityFilled { get; init; }

    [JsonPropertyName("L")]
    public decimal LastPriceFilled { get; init; }

    [JsonPropertyName("n")]
    public decimal Fee { get; init; }

    [JsonPropertyName("N")]
    public string FeeAsset { get; init; } = string.Empty;

    [JsonPropertyName("T")]
    [JsonConverter(typeof(MillisecondEpochDateTimeConverter))]
    public DateTime UpdateTime { get; init; }

    [JsonPropertyName("t")]
    public long TradeId { get; init; }

    [JsonPropertyName("w")]
    public bool IsWorking { get; init; }

    [JsonPropertyName("m")]
    public bool BuyerIsMaker { get; init; }

    [JsonPropertyName("O")]
    [JsonConverter(typeof(MillisecondEpochDateTimeConverter))]
    public DateTime CreateTime { get; init; }

    [JsonPropertyName("Z")]
    public decimal QuoteQuantityFilled { get; init; }

    [JsonPropertyName("Q")]
    public decimal QuoteQuantity { get; init; }

    [JsonPropertyName("Y")]
    public decimal LastQuoteQuantity { get; init; }

    [JsonPropertyName("g")]
    public long OrderListId { get; init; }

    [JsonPropertyName("I")]
    public long I { get; init; }

    [JsonPropertyName("M")]
    public bool M { get; init; }
}

public Task<SubscriptionToken> SubscribeToUserDataUpdatesAsync(string listenKey,
    Func<BinanceStreamOrderUpdate, ValueTask> onOrderUpdateMessage,
    Func<BinanceStreamOrderList, ValueTask> onOcoOrderUpdateMessage,
    Func<BinanceStreamPositionsUpdate, ValueTask> onAccountPositionMessage,
    Func<BinanceStreamBalanceUpdate, ValueTask> onAccountBalanceUpdate)
{
    var handler = new Func<string, ValueTask>(data =>
    {
        using var document = JsonDocument.Parse(data);

        if (!document.RootElement.TryGetProperty("data", out var dataElement))
        {
            return ValueTask.CompletedTask;
        }

        if (dataElement.TryGetProperty("e", out var eElement))
        {
            var e = eElement.GetString();

            switch (e)
            {
                case "outboundAccountPosition":
                    break;
                case "balanceUpdate":
                    break;
                case "executionReport":
                    var result = dataElement.Deserialize<BinanceStreamOrderUpdate>();
                    return onOrderUpdateMessage(result!);
                case "listStatus":
                    break;
            }
        }

        return ValueTask.CompletedTask;
    });

    return _client.SubscribeAsync(listenKey, handler);
}

Upvotes: 0

Views: 369

Answers (1)

tmaj
tmaj

Reputation: 35037

Side, SpotOrderType, etc. really look like enums. You can use the native JsonStringEnumConverter to do the work:

public enum Side {
    Buy = 0,
    Sell  = 1
};

public record BinanceStreamOrderUpdate
{
    [JsonConverter(typeof(JsonStringEnumConverter))] 
    [JsonPropertyName("S")]
    public Side? Side { get; init; }
};


    
public static async Task Main()
{
    var json = "{ \"S\": \"BUY\"}";

    var o = JsonSerializer.Deserialize<BinanceStreamOrderUpdate>(json);

    Console.WriteLine(o);
}

This prints:

BinanceStreamOrderUpdate { Side = Buy }

Upvotes: 1

Related Questions