Проблемы с десериализацией ответа Json на вызов API. .NET 6

using System.Net.Http.Headers;
using static System.Math;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Net.Http.Json;
using System.Net;

using HttpClient client = new();

await ProcessRepositoriesAsync(client);

static async Task ProcessRepositoriesAsync(HttpClient client)
{
    string apiUrl = "<<API CALL URL WITH AUTHENTICATION KEY>>";
    try
    {
        string jsonResponse = await client.GetStringAsync(apiUrl);
        var jsonDocument = JsonDocument.Parse(jsonResponse);

        if (jsonDocument.RootElement.TryGetProperty("response", out var responseData))
        {
            
            string jsonContent = File.ReadAllText(responseData.ToString());

            List<FlightData> flightList = JsonSerializer.Deserialize<List<FlightData>>(jsonContent);

            foreach (var flight in flightList)
            {
                Console.WriteLine($"Flight Number: {flight.flag}, Altitude: {flight.alt}");
            }
        }
        else
        {
            Console.WriteLine("No 'response' field found in JSON.");
        }
    }
    catch (HttpRequestException ex)
    {
        Console.WriteLine($"Error: {ex.Message}");
    }
    catch (JsonException ex)
    {
        Console.WriteLine($"JSON Deserialization Error: {ex.Message}");
    }
}
internal class FlightData
{
    public string? hex { get; set; }
    public string? reg_number { get; set; }
    public string? flag { get; set; }
    public string? lat { get; set; }
    public string? lng { get; set; }
    public string? alt { get; set; }
    public string? dir { get; set; }
    public string? speed { get; set; }
    public string? v_speed { get; set; }
    public string? squawk { get; set; }
    public string? flight_number { get; set; }
    public string? flight_icao { get; set; }
    public string? flight_iata { get; set; }
    public string? dep_icao { get; set; }
    public string? dep_iata { get; set; }
    public string? airline_icao { get; set; }
    public string? airline_iata { get; set; }
    public string? aircraft_icao { get; set; }
    public string? updated { get; set; }
    public string? status { get; set; }

    public FlightData(string hex, string reg_number, string flag, string lat, string lng, string alt, string dir, string speed, string v_speed, string flight_number, string flight_icao, string flight_iata, string dep_icao, string dep_iata, string airline_icao,
        string airline_iata, string aircraft_icao, string updated, string status)
    {
        this.hex = hex;
        this.reg_number = reg_number;
        this.flag = flag;
        this.lat = lat;
        this.lng = lng;
        this.alt = alt;
        this.dir = dir;
        this.speed = speed;
        this.v_speed = v_speed;
        this.flight_number = flight_number;
        this.flight_icao = flight_icao;
        this.flight_iata = flight_iata;
        this.dep_icao = dep_icao;
        this.dep_iata = dep_iata;
        this.airline_icao = airline_icao;
        this.airline_iata = airline_iata;
        this.aircraft_icao = aircraft_icao;
        this.updated = updated;
        this.status = status;

    }
}

(Мне также не нужно и не нужно КАЖДОЕ отдельное свойство, которое есть в конструкторе. Я просто добавил все, чтобы посмотреть, поможет ли это.) Код может быть немного шатким и выглядеть не очень хорошо. Я успешно сделал это на Python, но мне нужно сделать это на C#. Все это в новинку для меня, и я часами изучал документацию Microsoft, YouTube, GitHub, StackOverflow и т. д.

Я вызываю API, который дает мне данные о полете в реальном времени в ответе JSON. Мой план состоит в том, чтобы взять это и преобразовать каждую запись JSON в объект FlightData. Проблема, с которой я столкнулся (я думаю), заключается в том, что в верхней части ответа JSON содержится тонна метаданных. Меня это не волнует, пока не будет написано «ответ», после чего будут мои данные. Я думаю, что он пытается разобрать метаданные в объект и попадает во вторую ловушку, потому что атрибуты не совпадают. Оператор if помогает мне пройти мимо этого, но тогда он не работает должным образом. Это зависает мой компьютер. Я успешно вызвал API и получил данные.

В питоне все, что я сделал, было

allPlanes = json.loads(data)
for i in allPlanes['response']:
    try: ....

и это сработало хорошо.

Я ценю любую помощь и прошу прощения, если плохо объяснил. Если я выдам важную информацию, пожалуйста, не стесняйтесь сказать мне.

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


Обновлено: вот в чем ошибка, когда она попадает во второй улов:

Ошибка десериализации JSON: значение JSON не может быть преобразовано в FlightData. Путь: $[0].lat | Номер строки: 0 | Байтпозитионинлайн: 68.


Редактировать 2: спасибо за быстрые ответы, ребята. Не знаю, как я забыл добавить образец JSON.

Вот один без заголовка/метаданных (честно говоря, я не уверен, что это за точный термин):

[
   {
      "hex":"142329",
      "reg_number":"RA-09001",
      "flag":"RU",
      "lat":56.33128,
      "lng":79.944679,
      "alt":10980,
      "dir":272,
      "speed":872,
      "v_speed":0,
      "squawk":"5257",
      "flight_number":"9634",
      "flight_icao":"GZP9634",
      "flight_iata":"4G9634",
      "dep_icao":"UNTT",
      "dep_iata":"TOF",
      "airline_icao":"GZP",
      "airline_iata":"4G",
      "aircraft_icao":"F900",
      "updated":1692074910,
      "status":"en-route"
   },
   {
      "hex":"152000",
      "reg_number":"RA-73728",
      "flag":"RU",
      "lat":56.401016,
      "lng":45.294384,
      "alt":10355,
      "dir":260,
      "speed":779,
      "v_speed":0,
      "squawk":"5527",
      "flight_number":"1437",
      "flight_icao":"AFL1437",
      "flight_iata":"SU1437",
      "dep_icao":"USSS",
      "dep_iata":"SVX",
      "arr_icao":"UUEE",
      "arr_iata":"SVO",
      "airline_icao":"AFL",
      "airline_iata":"SU",
      "aircraft_icao":"A321",
      "updated":1692074910,
      "status":"en-route"
   }
]

А вот верхняя часть с записью:

{
   "request":{
      "lang":"en",
      "currency":"USD",
      "time":33,
      "id":"6u6cys0kh2o",
      "server":"l",
      "host":"airlabs.co",
      "pid":1867027,
      "key":{
         "id":26237,
         "api_key":"2354963e-6fc3-4226-a220-46cb2bab6633",
         "type":"free",
         "expired":"2023-09-10T00:00:00.000Z",
         "registered":"2023-08-11T04:11:07.000Z",
         "limits_by_hour":2500,
         "limits_by_minute":250,
         "limits_by_month":1000,
         "limits_total":884
      },
      "params":{
         "lang":"en"
      },
      "version":9,
      "method":"flights",
      "client":{
         "ip":"2600:6c5c:6c00:4d9:804e:66ab:1add:d35",
         "geo":{
            "country_code":"US",
            "country":"United States",
            "continent":"North America",
            "city":"Kingsport",
            "lat":36.5491,
            "lng":-82.5584,
            "timezone":"America/New_York"
         },
         "connection":{
            "isp_code":20115,
            "isp_name":"Charter Communications"
         },
         "device":{
            
         },
         "agent":{
            
         },
         "karma":{
            "is_blocked":false,
            "is_crawler":false,
            "is_bot":false,
            "is_friend":false,
            "is_regular":true
         }
      }
   },
   "response":[
      {
         "hex":"142329",
         "reg_number":"RA-09001",
         "flag":"RU",
         "lat":56.307404,
         "lng":81.793709,
         "alt":10492,
         "dir":268,
         "speed":861,
         "v_speed":3.3,
         "squawk":"5257",
         "flight_number":"9634",
         "flight_icao":"GZP9634",
         "flight_iata":"4G9634",
         "dep_icao":"UNTT",
         "dep_iata":"TOF",
         "airline_icao":"GZP",
         "airline_iata":"4G",
         "aircraft_icao":"F900",
         "updated":1692074433,
         "status":"en-route"
      },
      {
         "hex":"152000",
         "reg_number":"RA-73728",
         "flag":"RU",
         "lat":56.593597,
         "lng":46.8978,
         "alt":10363,
         "dir":250,
         "speed":759,
         "v_speed":0,
         "squawk":"5527",
         "flight_number":"1437",
         "flight_icao":"AFL1437",
         "flight_iata":"SU1437",
         "dep_icao":"USSS",
         "dep_iata":"SVX",
         "arr_icao":"UUEE",
         "arr_iata":"SVO",
         "airline_icao":"AFL",
         "airline_iata":"SU",
         "aircraft_icao":"A321",
         "updated":1692074433,
         "status":"en-route"
      }
   ]
}

Добавление образца json также было бы неплохо.

Eldar 15.08.2023 07:35
File.ReadAllText принимает имя файла в качестве параметра. Не уверен, что вам это нужно.
Orifjon 15.08.2023 07:50

А поскольку вы используете геттеры и сеттеры для FlightData, вам не нужен конструктор с параметрами.

Orifjon 15.08.2023 07:54

Просто из любопытства - правильно ли я понимаю, что вы запрашиваете какой-то API, а затем читаете локальный файл на основе ответа?

Guru Stron 15.08.2023 08:12

Что касается проблемы - просто предположение - public string? lat { get; set; } должно быть что-то вроде public double? lat { get; set; }. System.Text.Json довольно требователен к используемым правильным типам (т. е. он не будет считывать числа в строки по умолчанию). Ошибка предполагает, что существует несоответствие типов. P.S. пожалуйста, предоставьте образец JSON (т.е. минимальный воспроизводимый пример)

Guru Stron 15.08.2023 08:13

Добавил JSON. Извините за это, ребята. Спасибо за ответы.

Chris 15.08.2023 08:42

@GuruStron Честно говоря, я не знаком с терминологией. Я вызываю API, и он возвращает гигантский ответ, полный текста. Я не думаю, что это файл?

Chris 15.08.2023 08:45

@Chris Здесь не так много терминологии. У вас есть string jsonContent = File.ReadAllText(responseData.ToString()); в вашем коде. File.ReadAllText будет читать текст из ... файла =)

Guru Stron 15.08.2023 08:46

@GuruStron Понятно. Эта часть кода была еще одной попыткой попробовать что-то другое. Когда я использую его, он зависает vsstudio. Когда я удаляю его, я получаю ошибки JSON. Что, если я превращу все это в гигантскую строку и разделю/обрежу на основе запятых и скобок/скобок?

Chris 15.08.2023 08:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Итак, если предположить, что второй JSON является строкой ответа, вам нужно исправить типы в целевом классе (как я писал в комментариях):

public class FlightData
{
    [JsonPropertyName("hex")] 
    public string Hex { get; set; }

    [JsonPropertyName("reg_number")] 
    public string RegNumber { get; set; }

    [JsonPropertyName("flag")] 
    public string Flag { get; set; }

    [JsonPropertyName("lat")] 
    public double Lat { get; set; }

    [JsonPropertyName("lng")] 
    public double Lng { get; set; }

    [JsonPropertyName("alt")] 
    public long Alt { get; set; }

    [JsonPropertyName("dir")] 
    public long Dir { get; set; }

    [JsonPropertyName("speed")]
    public long Speed { get; set; }

    [JsonPropertyName("v_speed")] 
    public double VSpeed { get; set; }

    [JsonPropertyName("squawk")] 
    public string Squawk { get; set; }

    [JsonPropertyName("flight_number")] 
    public string FlightNumber { get; set; }

    [JsonPropertyName("flight_icao")] 
    public string FlightIcao { get; set; }

    [JsonPropertyName("flight_iata")]
    public string FlightIata { get; set; }

    [JsonPropertyName("dep_icao")]
    public string DepIcao { get; set; }

    [JsonPropertyName("dep_iata")]
    public string DepIata { get; set; }

    [JsonPropertyName("airline_icao")]
    public string AirlineIcao { get; set; }

    [JsonPropertyName("airline_iata")]
    public string AirlineIata { get; set; }

    [JsonPropertyName("aircraft_icao")]
    public string AircraftIcao { get; set; }

    [JsonPropertyName("updated")]
    public long Updated { get; set; }

    [JsonPropertyName("status")]
    public string Status { get; set; }

    [JsonPropertyName("arr_icao")]
    public string ArrIcao { get; set; }

    [JsonPropertyName("arr_iata")]
    public string ArrIata { get; set; }
}

И затем вы можете ввести Root и просто использовать его:

class Root
{
    [JsonPropertyName("response")]
    public List<FlightData> Response { get; set; }
}

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

var deserialize = JsonSerializer.Deserialize<Root>(jsonResponse);

Или просто:

var result = await client.GetFromJsonAsync<Root>(...)

Если вы непреклонны в проверке собственности, используйте:

using var jsonDocument = JsonDocument.Parse(jsonResponse); // DO NOT FORGET USING!
if (jsonDocument.RootElement.TryGetProperty("response", out var responseData))
{
    var result = responseData.Deserialize<List<FlightData>>();
}

P.S.

Существует множество инструментов, позволяющих преобразовывать JSON в типы C#, некоторые из них встроены в IDE, или вы можете использовать онлайн-инструменты, такие как app.quicktype.io

Это сработало чудесно. Я очень ценю это! Я был на этом в течение нескольких часов. Если бы я хотел прочитать о том, что решило проблему, что бы я искал? Кроме того, это не связано, но очень быстро. В некоторых записях в JSON отсутствуют определенные поля, например, может отсутствовать шестнадцатеричный код или любая комбинация. Поскольку свойства FlightData могут принимать значения NULL, он просто оставит их пустыми и заполнит то, что может, с помощью Json Deserialize?

Chris 15.08.2023 09:18

@Chris был рад помочь! Не уверен, что это описано в документации, TBH. «он просто оставит эти пустые и заполнит то, что может, с помощью Json» - да, сериализатор будет использовать значение по умолчанию (обратите внимание, что для таких типов значений, как int, вам может потребоваться использовать их аналог, допускающий значение NULL - int?, поиск типов значений, допускающих значение NULL ). Также обратите внимание, что вы также можете использовать Newtonsoft Json.NET, который долгое время был стандартом де-факто.

Guru Stron 15.08.2023 10:09

Другие вопросы по теме