1

So I am using TDAmeritrade API to receive stock data with a C# Winforms program on Visual Studio. It takes the user input stock symbol and searches for the info. I am using HttpClient and Newtonsoft.Json and have been able to successfully perform the GET request and receive a JSON string back, but I do not know how to get all of the information I need out of it.

Here is the JSON: https://drive.google.com/file/d/1TpAUwjyqrHArEXGXMof_K1eQe0hFoaw5/view?usp=sharing

Above is the JSON string sent back to me then formatted. My goal is to record information for each price in "callExpDateMap.2021-02-19:11" and "callExpDateMap.2021-03-19:39". The problem is that for each different stock, the dates that show up in "callExpDateMap" are going to be different.

client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
            var response = await client.GetAsync(url);
            var info = await response.Content.ReadAsStringAsync();

            dynamic config = JsonConvert.DeserializeObject<dynamic>(info, new ExpandoObjectConverter());
            return config;

This is the code I have right now. I know the last for statement is not correct. How can I parse to the specific sections I want (callExpDateMap.expirationdate.StrikePrice) and get the information needed from each without knowing the dates and Strike prices beforehand? Is there a way to innumerate it and search through the JSON as if it were all a bunch of arrays?

3
  • 2
    Can you add an example of your Json rather than a screenshot of it Commented Feb 8, 2021 at 18:11
  • 1
    Parse Json the only difference is that you need property in DTO which is List of Lists of Lists Commented Feb 8, 2021 at 18:14
  • Deserialize to Dictionary<Dictionary<InnerObject>>, the dates will be in the outer object, each stock in the next level, the inner object takes the innermost key/values Commented Feb 8, 2021 at 20:41

2 Answers 2

4

The code below is perhaps not the most elegant nor complete, but I think it will get you going. I would start by using the JObject.Parse() from the Newtonsoft.Json.Linq namespace and take it from there.

JObject root = JObject.Parse(info);
string symbol = root["symbol"].ToObject<string>();
foreach (JToken toplevel in root["callExpDateMap"].Children())
{
    foreach (JToken nextlevel in toplevel.Children())
    {
        foreach (JToken bottomlevel in nextlevel.Children())
        {
            foreach (JToken jToken in bottomlevel.Children())
            {
                JArray jArray = jToken as JArray;
                foreach (var arrayElement in jArray)
                {
                    InfoObject infoObject = arrayElement.ToObject<InfoObject>();
                    Console.WriteLine(infoObject.putCall);
                    Console.WriteLine(infoObject.exchangeName);
                    Console.WriteLine(infoObject.multiplier);
                }
            }
        }
    }
}

public class InfoObject
{
    public string putCall { get; set; }
    public string symbol { get; set; }
    public string description { get; set; }
    public string exchangeName { get; set; }
    // ...
    public int multiplier { get; set; }
    // ...
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much that is what I needed. Using the Jtoken for each level to prevent having to strictly name each property allowed me to parse through the information perfectly.
3

This is official documentation of Newtonsoft method you are trying to use.

https://www.newtonsoft.com/json/help/html/Overload_Newtonsoft_Json_JsonConvert_DeserializeObject.htm

enter image description here

If an API's method returns different json propeties and you cannot trust it's property names all the times, then you can try using a deserialize method that returns .Net object, for example: JsonConvert.DeserializeObject Method (String) https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_DeserializeObject.htm

That method's signature is this: public static Object DeserializeObject(string value)

Parameter is: value of type json string.

Return Value is: Object of type object.


If you do not want an Object, then you can of course use a .Net type you have. Such as this method: JsonConvert.DeserializeObject Method (String)

Any property that you have in both (the .net type and json object) will get populated. If .net type has properties that do not exist in json object, then those will be ignored. If json object has properties that do not exist in.net, then those will be ignored too.

Here's an example of a .Net type

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace MyNameSpace
{
    public class TDAmeritradeStockData
    {
        [JsonProperty("symbol")]
        public string Symbol { get; set; }
        [JsonProperty("status")]
        public string Status { get; set; }
        [JsonProperty("callExpDateMap")]
        public object CallExpDateMap { get; set; }
        //...
        //...

        public CallExpDateMapType[] CallExpDateMapList { get; set; }
    }

    public class CallExpDateMapType
    {
        [JsonProperty("expirationdate")]
        public string Expirationdate { get; set; }
        [JsonProperty("StrikePrice")]
        public List<StrikePriceType> StrikePriceList { get; set; }
    }

    public class StrikePriceType
    {
        public string StrikePrice { get; set; }
        public List<StrikePricePropertiesType> StrikePricePropertiesList { get; set; }
    }

    public class StrikePricePropertiesType
    {
        [JsonProperty("putCall")]
        public string PutCall { get; set; }
        [JsonProperty("symbol")]
        public string Symbol { get; set; }
        [JsonProperty("description")]
        public string Description { get; set; }
        [JsonProperty("exchangeName")]
        public string ExchangeName { get; set; }
        [JsonProperty("bid")]
        public double Bid { get; set; }
        [JsonProperty("ask")]
        public double Ask { get; set; }
        //...
        //...
    }

    [TestClass]
    public class TestTestTest
    {
        [TestMethod]
        public void JsonTest()
        {
            var jsondata = ReadFile("data.json");

            var model = JsonConvert.DeserializeObject<TDAmeritradeStockData>(jsondata);

            JObject jObject = (JObject)model.CallExpDateMap;

            var count = ((JObject)model.CallExpDateMap).Count;
            model.CallExpDateMapList = new CallExpDateMapType[count];

            var jToken = (JToken)jObject.First;

            for (var i = 0; i < count; i++)
            {
                model.CallExpDateMapList[i] = new CallExpDateMapType
                {
                    Expirationdate = jToken.Path,
                    StrikePriceList = new List<StrikePriceType>()
                };

                var nextStrikePrice = jToken.First.First;

                while (nextStrikePrice != null)
                {
                    var nextStrikePriceProperties = nextStrikePrice;

                    var srikePriceList = new StrikePriceType
                    {
                        StrikePrice = nextStrikePriceProperties.Path,
                        StrikePricePropertiesList = JsonConvert.DeserializeObject<List<StrikePricePropertiesType>>(nextStrikePrice.First.ToString())
                    };

                    model.CallExpDateMapList[i].StrikePriceList.Add(srikePriceList);

                    nextStrikePrice = nextStrikePrice.Next;
                }

                jToken = jToken.Next;

            }

            Assert.IsNotNull(model);
        }

        private string ReadFile(string fileName)
        {
            using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                var data = new StringBuilder();
                using (var streamReader = new StreamReader(fileStream))
                {
                    while (!streamReader.EndOfStream) data.Append(streamReader.ReadLine());
                    streamReader.Close();
                }
                fileStream.Close();
                return data.ToString();
            }
        }
    }  
}

2 Comments

This is helpful, but the thing is that "expirationdate" and "Strikeprice" are not the names of the properties in JSON. See the updated question with a link to the entire JSON. Each expiration date and price will just be a number that differs depending on the stock chosen. I want to create a code that will give me information for each price at each date, no matter what stock is chosen.
I created a unit test class and used your data.json file in order to validate. If you execute this unit test as is, C# model will populate and you will be able to see that the data exists now. As per your needs, you will need to create more properties in your classes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.