0

I receive a string in this format:

   {
  "ok": true,
  "quote": { // the below is the same as returned through the REST quote API
    "symbol": "FAC",
    "venue": "OGEX",
    "bid": 5100, // best price currently bid for the stock
    "ask": 5125, // best price currently offered for the stock
    "bidSize": 392, // aggregate size of all orders at the best bid
    "askSize": 711, // aggregate size of all orders at the best ask
    "bidDepth": 2748, // aggregate size of *all bids*
    "askDepth": 2237, // aggregate size of *all asks*
    "last": 5125, // price of last trade
    "lastSize": 52, // quantity of last trade
    "lastTrade": "2015-07-13T05:38:17.33640392Z", // timestamp of last trade,
    "quoteTime": "2015-07-13T05:38:17.33640392Z" // server ts of quote generation
  }
}

I want to use the Newtonsoft.Json.Linq way of deserializing for performance reasons. When I try to turn the Json string into a Quote object using the following method:

 public static Quote QuoteFromJson(string quoteJson)
        {
            var json = JObject.Parse(quoteJson);
            var quote = json["quote"].
                Select(q => new Quote
                {
                    Ask = int.Parse(q["ask"].ToString()),
                    AskDepth = int.Parse(q["askDepth"].ToString()),
                    AskSize = int.Parse(q["askSize"].ToString()),
                    Bid = int.Parse(q["bid"].ToString()),
                    BidDepth = int.Parse(q["bidDepth"].ToString()),
                    BidSize = int.Parse(q["bidSize"].ToString()),
                    Last = int.Parse(q["last"].ToString()),
                    LastSize = int.Parse(q["lastSize"].ToString()),
                    LastTrade = q["lastTrade"].ToString(),
                    QuoteTime = q["quoteTime"].ToString(),
                    Symbol = q["symbol"].ToString(),
                }).First();

            return quote;
        }

This gives an error message:

Cannot access child value on Newtonsoft.Json.Linq.JProperty

What am I doing wrong?

yesman
  • 7,165
  • 15
  • 52
  • 117
  • 2
    Why don't you directly parse it to a `Quote`? Like this: `JsonConvert.DeserializeObject(quoteJson)` – Fᴀʀʜᴀɴ Aɴᴀᴍ Dec 23 '15 at 21:16
  • That would make life easy, but DeserializeObject uses Reflection, which slows down my app considerably. – yesman Dec 23 '15 at 21:24
  • 2
    Slower than dipping to a JObject property by property to construct your object? – Ňɏssa Pøngjǣrdenlarp Dec 23 '15 at 21:32
  • 3
    If Json.NET isn't fast enough for you, I suggest you look into other libraries before resorting to your current method. This is very messy. I would also suggest you set performance goals based on business needs, not just by looking at a profiler going, "OH MAN I SPENT SO MUCH TIME DESERIALIZING". Chances are you are spending WAY more time actually doing network IO than you are deserializing. – Pete Garafano Dec 23 '15 at 21:34
  • Linq to JSON might not be faster than deserialization. See [this answer](http://stackoverflow.com/questions/34279454/how-to-use-json-netjobject-jarray-jtoken-and-convert-to-class-in-the-fastest-w/34279736#34279736) for a comparison of deserialization vs direct parsing. – dbc Dec 23 '15 at 21:36

2 Answers2

1

Here is a direct answer to your question:

The quote variable corresponds to the quote token in your JSON. This is a single item and not a collection, so you shouldn't treat it as a collection and use the Select method.

Instead, access it directly like this:

var json = JObject.Parse(quoteJson);

var quote = json["quote"];

var result = new Quote
{
    Ask = int.Parse(quote["ask"].ToString()),
    AskDepth = int.Parse(quote["askDepth"].ToString()),
    AskSize = int.Parse(quote["askSize"].ToString()),
    Bid = int.Parse(quote["bid"].ToString()),
    BidDepth = int.Parse(quote["bidDepth"].ToString()),
    BidSize = int.Parse(quote["bidSize"].ToString()),
    Last = int.Parse(quote["last"].ToString()),
    LastSize = int.Parse(quote["lastSize"].ToString()),
    LastTrade = quote["lastTrade"].ToString(),
    QuoteTime = quote["quoteTime"].ToString(),
    Symbol = quote["symbol"].ToString(),
};
Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
1

Your problem is that you are iterating through the children of "quote" with your Select, then retrieving their properties by name. This would be appropriate if "quote" were an array, but it's not -- it's already a single object. Thus you should do:

        var rootObj = JObject.Parse(quoteJson);
        var quoteObj = rootObj["quote"];
        var quote = new Quote
        {
            Ask = (int)quoteObj["ask"],
            AskDepth = (int)quoteObj["askDepth"],
            AskSize = (int)quoteObj["askSize"],
            Bid = (int)quoteObj["bid"],
            BidDepth = (int)quoteObj["bidDepth"],
            BidSize = (int)quoteObj["bidSize"],
            Last = (int)quoteObj["last"],
            LastSize = (int)quoteObj["lastSize"],
            LastTrade = (string)quoteObj["lastTrade"],
            QuoteTime = (string)quoteObj["quoteTime"],
            Symbol = (string)quoteObj["symbol"],
        };

Note I am using explicit casting. This handles internationalization of numbers in a manner consistent with the JSON standard. int.Parse() parses using localized formatting, which is not guaranteed to be consistent with the standard in all locales.

However, it is much simpler to use deserialization:

        var rootObj = JObject.Parse(quoteJson);
        var quoteObj = rootObj["quote"];
        var quote = quoteObj.ToObject<Quote>();

I suggest you should time this to make sure it really is slower than Linq to JSON.

dbc
  • 104,963
  • 20
  • 228
  • 340