Парсинг значения json с помощью Newtonsoft на с#

346
25 марта 2018, 18:33

Задача: получить значение "lastPriceProtected".

С мопощью ClientWebSocket получаю json, он различный:

ответ 1

{"table":"instrument","action":"update","data":[{"symbol":"XBTUSD","lastTickDirection":"ZeroMinusTick","timestamp":"2018-03-22T20:15:45.258Z"}]}

ответ 2

{"table":"instrument","action":"update","data":[{"symbol":"XBTUSD","lastPrice":8569,"lastPriceProtected":8569.7146,"lastChangePcnt":-0.0362,"timestamp":"2018-03-22T20:15:45.350Z"}]}

по этому однозначно получить значение "lastPriceProtected" не получается так как в первой строке данного параметра нет. т.е. как бы я не формировал поиск поля постоянно получаю исключение: "System.NullReferenceException: "Ссылка на объект не указывает на экземпляр объекта.""

мой код:

JObject o = JObject.Parse(str);
JToken firstProductNames = o["data"].SelectToken("lastPriceProtected");

======================================================================= после чтения документации и нескольких попыток получил рабочий код, спасибо всем кто помогал.

            JObject o1 = JObject.Parse(str);
            if (o1["data"] != null)
            {
                var firstProductNames = o1["data"].Value<JArray>().ElementAt(0).SelectToken("lastPriceProtected");
                if (firstProductNames != null)
                {
                    float value = firstProductNames.Value<float>();
                    Debug.WriteLine(value + "--1");
                }
            }

if (o1["data"] != null) проверка нужна чтобы не получать исключения.

Answer 1

Советую вам создать полноценную структуру данных, некий класс, где будут все возможные значения из вашего JSON.

То есть, возьмем 2-ой JSON и сделаем два класса:

public class Data
{
    [JsonProperty("symbol")]
    public string Symbol { get; set; }
    [JsonProperty("lastPrice")]
    public long LastPrice { get; set; }
    [JsonProperty("lastPriceProtected")]
    public double LastPriceProtected { get; set; }
    [JsonProperty("lastChangePcnt")]
    public double LastChangePcnt { get; set; }
    [JsonProperty("timestamp")]
    public System.DateTimeOffset Timestamp { get; set; }
}
public class Root
{
    [JsonProperty("table")]
    public string Table { get; set; }
    [JsonProperty("action")]
    public string Action { get; set; }
    [JsonProperty("data")]
    public Data[] Data { get; set; }
}

Как можно заметить, тут имеются все возможные значения приведенные в красивый вид с красивыми именами.

Далее нам надо десериализовать данные:

var json = JsonConvert.DeserializeObject<Root>(jsonString);

Все, можно работать. Все данные, которые имеются в JSON - будут занесены в переменные, а те, которые пустые - будут иметь стандартные значения (то есть null, 0, false и т.д.). Если не хотим путаться и не заполненные данные хотите получить в NULL значение, то можете добавить знак ? после типа (к примеру public double LastPriceProtected { get; set; } меняем на public double? LastPriceProtected { get; set; }).

Ну теперь простейший вывод:

foreach (var value in json.Data)
{
    Console.WriteLine(value.LastPriceProtected); //Либо делаем проверку на необходимо на NULL/стандартное значение.
}

На выходе получим 0, либо значение (в вашем случае это 8569,7146).

По поводу минимализма и практичности...

1. Использование JObject:

Маленькое приложение, где мы выводим различные данные (как в основном "теле" программы, так и в другом классе):

namespace ConsoleApp2
{
    public class SumData
    {
        private JObject Json;
        public SumData(JObject json)
        {
            Json = json;
        }
        public int Sum()
        {
            return (int) Json["data"]["valOne"] + (int) Json["data"]["valOne"];
        }
    }
    class Program
    {
        private static JObject json;
        static void Main(string[] args)
        {
            string jString = "...";
            json = JObject.Parse(jString);
            Console.WriteLine(json["Hello"]);
            ListedObjects();

            SumData sumData = new SumData(json);
            Console.WriteLine(sumData.Sum());
            Console.ReadKey();
        }
        private static void ListedObjects()
        {
            foreach (var val in json["Collection"])
            {
                Console.WriteLine(val["dataObject"]);
                Console.WriteLine(val["dataObject2"]);
            }
        }
    }
}

К примеру на сервере изменилась структура, все данные переименовались. Что делать? Выход - искать где крашется приложение и руками изменять все значения.

Минусы такого подхода в том, что вы не получите ошибку, пока не вызовите эти данные. То есть вы можете что то упустить при редактирование, да и кода править придется много.

Плюсы такого подхода:

  • Не нужна структура классов.

Минусы:

  • Мы не знаем имена, которые нам доступны, забудем - беда..
  • Трудно редактировать в коде.
  • Если что то поменялось на сервер, то мы не узнаем это, пока не вызовем. То есть нам трудно будет отловить все переменные, можем где то забыть переименовать и среда разработки нам об этом не скажет.
  • Не очень понятные имена, зачастую сервера дают значения вида o, h.

2. Использование Структуры классов:

Точно такая же задача, все точно такое же, но с классами:

namespace ConsoleApp2
{
    public class RootJson
    {
        public string Hello { get; set; }
        public Collection[] Collection { get; set; }
        public Data Data { get; set; }
    }
    public class Collection
    {
        [JsonProperty("jsonServerValue")]
        public int dataObject { get; set; }
        public int dataObject2 { get; set; }
    }
    public class Data
    {
        public int valOne { get; set; }
        public int valTwo { get; set; }
    }
    public class SumData
    {
        private RootJson Json;
        public SumData(RootJson json)
        {
            Json = json;
        }
        public int Sum()
        {
            return Json.Data.valOne + Json.Data.valTwo;
        }
    }
    class Program
    {
        private static RootJson json;
        static void Main(string[] args)
        {
            string jString = "...";
            json = JsonConvert.DeserializeObject<RootJson>(jString);
            Console.WriteLine(json.Hello);
            ListedObjects();

            SumData sumData = new SumData(json);
            Console.WriteLine(sumData.Sum());
            Console.ReadKey();
        }
        private static void ListedObjects()
        {
            foreach (var val in json.Collection)
            {
                Console.WriteLine(val.dataObject);
                Console.WriteLine(val.dataObject2);
            }
        }
    }
}

Что имеем? Если сервер поменяет структуру, то нам достаточно поменять пару строк в нашем классе, либо вовсе с помощью [JsonProperty("...")] мы можем указать имя переменной и нечего не трогать.

Плюсы такого подхода:

  • Тип, мы его сразу знаем, не надо сто раз конвертировать.
  • Если поменяются данные, то не потеряетесь при изменение, среда разработки подскажет что и где не так.
  • Удобные имена.
  • Удобное использование.
  • Знаем все доступные переменные, не надо вспоминать имя.

Минусы:

  • Ну, может быть большая "портянка" из структуры классов, но ИМХО, положил в файлик .cs да забыл.
Answer 2

У вас:

"data":[{"symbol":"XBTUSD","lastPrice":8569,"lastPriceProtected":8569.7146,"lastChangePcnt":-0.0362,"timestamp":"2018-03-22T20:15:45.350Z"}]

массив, а вы пытаетесь работать как с объектом.

Надо так:

JObject o1 = JObject.Parse(str);
JToken firstProductNames = o1["data"].Value<JArray>().GetItem(0).SelectToken("lastPriceProtected");
if (firstProductNames != null) {
    float value = firstProductNames.Value<float>();
}
READ ALSO
c# awesomium отобразить webview в webcontrol

c# awesomium отобразить webview в webcontrol

Здравствуйте, подскажите, пожалуйста, использую awesomium с помощью webview

209
c# парсинг файлов multipart form data

c# парсинг файлов multipart form data

клиент на андроиде передает multipart/form-data данные серверу на c# строковые данные извлекаются без проблем, а вот файлы разобрать не могу android использует...

256
Как узнать находится ли курсор в форме?

Как узнать находится ли курсор в форме?

Необходимо понять находится ли курсор мыши на данный момент в форме, как лучше всего это сделать?

186