0

I want to deserialise the json string below which doesn't use property names for the bids or asks.

This is what my classes look like:

public class OrderBook
{
    public long lastUpdateId { get; set; }
    public List<Order> Bids { get; set; }
    public List<Order> Asks { get; set; }
}
public class Order
{
    public double Price { get; set; }
    public double Qty { get; set; }
}

This is what the json looks like.

{
  "lastUpdateId": 1027024,
  "bids": [
    [
      "4.00000000",
      "431.00000000"
    ]
  ],
  "asks": [
    [
      "4.00000200",
      "12.00000000"
    ]
  ]
}

When I try to deserialise I get the following error message:

"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Order' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly"

I thought maybe I should use a dictionary. But the bids or asks can have a duplicate key which is not allowed by a dictionary. Does anybody have a suggestion?

In the example I have only one bid and one ask but it is going to be multiple bids and multiple asks.

Ron Splinter
  • 151
  • 3
  • 14
  • 1
    are you using Newtonsoft for convertion? – Mansur Kurtov Jul 06 '21 at 11:07
  • If you add `[JsonProperty(Order = X)]` attributes to `Order`, you could use `ObjectToArrayConverter` from [this answer](https://stackoverflow.com/a/39462464/3744182) to [How to deserialize an array of values with a fixed schema to a strongly typed data class?](https://stackoverflow.com/q/39461518/3744182) to deserialize to `List` properties as required. In fact this looks like a duplicate, agree? – dbc Jul 06 '21 at 18:06
  • Also I recommend using `decimal` rather than `double` for monetary values. – dbc Jul 06 '21 at 18:16

3 Answers3

1

Please consider this as an addition to the other answers.

As @Mansur Kurtov correctly points to, it depends on what you're using for deserializing your JSON.

Let's test this case using both Newtonsoft and System.Text.Json.JsonSerializer.

When using JsonConvert, DTO from @RonSplinter's (OP) answer will work, but will fail when using JsonSerializer straight-forward. When applying these options to JsonSerializer, @RonSplinter's answer will work. Please note that this applies to .net Core 5 and above.

var serializeOptions = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    NumberHandling = JsonNumberHandling.AllowReadingFromString
};

To make tests run with both and without any further config, we'll have to do like in @Mansur Kurtov's answer.

Reason for this is that JsonSerializer is case-sensitive and "-sensitive.

Class OrderBookUsingJsonPropertyName below shows how to be conform with c# casing using JsonPropertyName attributes (honoured by JsonSerializer only).

public class OrderBookUsingDouble
{
    public long lastUpdateId { get; set; }
    public double[][] Bids { get; set; }
    public double[][] Asks { get; set; }
}

public class OrderBookUsingString
{
    public long lastUpdateId { get; set; }
    public string[][] bids { get; set; }
    public string[][] asks { get; set; }
}

public class OrderBookUsingJsonPropertyName
{
    [JsonPropertyName("lastUpdateId")] 
    public long LastUpdateId { get; set; }
    
    [JsonPropertyName("bids")] 
    public string[][] Bids { get; set; }

    [JsonPropertyName("asks")] 
    public string[][] Asks { get; set; }
}

public class JsonParserTests
{
    private const string Json = @"
    {
      ""lastUpdateId"": 1027024,
            ""bids"": [
            [
            ""4.00000000"",
            ""431.00000000""
            ]
            ],
            ""asks"": [
            [
            ""4.00000200"",
            ""12.00000000""
            ]
            ]
        }";

    [Fact]
    public void JsonConvert_OrderBookUsingDouble()
    {
        var orderBook = JsonConvert.DeserializeObject<OrderBookUsingDouble>(Json);
        Assert.Equal(2, orderBook.Bids[0].Length);
    }

    [Fact]
    public void JsonConvert_OrderBookUsingString()
    {
        var orderBook = JsonConvert.DeserializeObject<OrderBookUsingString>(Json);
        Assert.Equal(2, orderBook.bids[0].Length);
    }

    [Fact]
    public void JsonConvert_OrderBookUsingJsonPropertyName()
    {
        var orderBook = JsonConvert.DeserializeObject<OrderBookUsingJsonPropertyName>(Json);
        Assert.Equal(2, orderBook.Bids[0].Length);
    }

    [Fact]
    public void JsonSerializer_OrderBookUsingDouble_UsingOptions()
    {
        var serializeOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            NumberHandling = JsonNumberHandling.AllowReadingFromString
        };
        var orderBook = System.Text.Json.JsonSerializer.Deserialize<OrderBookUsingDouble>(Json, serializeOptions);
        Assert.Equal(2, orderBook.Bids[0].Length);
    }

    [Fact]
    public void JsonSerializer_OrderBookUsingString()
    {
        var orderBook = System.Text.Json.JsonSerializer.Deserialize<OrderBookUsingString>(Json);
        Assert.Equal(2, orderBook.bids[0].Length);
    }

    [Fact]
    public void JsonSerializer_OrderBookUsingJsonPropertyName()
    {
        var orderBook =
            System.Text.Json.JsonSerializer.Deserialize<OrderBookUsingJsonPropertyName>(Json);
        Assert.Equal(2, orderBook.Bids[0].Length);
    }
}
Roar S.
  • 8,103
  • 1
  • 15
  • 37
0

It is solved already. Just change the class to:

public class OrderBook
{
    public long lastUpdateId { get; set; }
    public double[][] Bids { get; set; }
    public double[][] Asks { get; set; }
}
Ron Splinter
  • 151
  • 3
  • 14
0

Use this class for Deserialization:

public class OrderBook
{
        public long lastUpdateId { get; set; }
        public List<List<string>> bids { get; set; }
        public List<List<string>> asks { get; set; }
}
Mansur Kurtov
  • 726
  • 1
  • 4
  • 12