如何在不知道 JSON 值的情况下动态解析一些 JSON?

问题描述

所以我使用 TdameriTrade API 在 Visual Studio 上通过 C# Winforms 程序接收股票数据。它需要用户输入股票代码搜索信息。我正在使用 HttpClient 和 Newtonsoft.Json,并且能够成功执行 GET 请求并接收回 JSON 字符串,但我不知道如何从中获取我需要的所有信息。

这是JSON: https://drive.google.com/file/d/1TpAUwjyqrHArEXGXMof_K1eQe0hFoaw5/view?usp=sharing

上面是发回给我然后格式化的 JSON 字符串。我的目标是在“callExpDateMap.2021-02-19:11”和“callExpDateMap.2021-03-19:39”中记录每个价格的信息。问题在于,对于每种不同的股票,“callExpDateMap”中显示的日期会有所不同。

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;

这是我现在拥有的代码。我知道最后一个 for 语句是不正确的。我如何解析我想要的特定部分 (callExpDateMap.expirationdate.StrikePrice) 并从每个部分获取所需的信息,而无需事先知道日期和行使价?有没有办法枚举它并搜索JSON,就好像它是一堆数组一样?

解决方法

这是您尝试使用的 Newtonsoft 方法的官方文档。

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

enter image description here

如果某个 API 的方法返回不同的 json 属性,并且您不能始终相信它的属性名称,那么您可以尝试使用返回 .Net 对象的反序列化方法,例如:JsonConvert.DeserializeObject Method (String) https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_DeserializeObject.htm

该方法的签名是这样的: 公共静态对象反序列化对象(字符串值)

参数为:json字符串类型的值。

返回值为:object 类型的对象。


如果您不想要一个对象,那么您当然可以使用您拥有的 .Net 类型。比如这个方法: JsonConvert.DeserializeObject 方法(字符串)

您同时拥有的任何属性(.net 类型和 json 对象)都将被填充。如果 .net 类型具有 json 对象中不存在的属性,则这些属性将被忽略。如果 json 对象具有 .net 中不存在的属性,那么这些属性也会被忽略。

这是一个 .Net 类型的例子

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();
            }
        }
    }  
}
,

下面的代码可能不是最优雅也不是最完整的,但我认为它会让你继续前进。我将首先使用 JObject.Parse() 命名空间中的 Newtonsoft.Json.Linq 并从那里获取它。

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; }
    // ...
}