2
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;

    }
}

(I also don't need or want EVERY single property that is in the constructor. I just added all to see if that would help.) The code may be a bit wonky and it might not look good. I successfully did this in Python but I need to do it in C#. This is all new to me and I've spent hours on Microsoft's documentation, YouTube, GitHub, Stack Overflow, etc.

I am calling an API that gives me real time flight data in a JSON response. My plan is to take that and convert every JSON entry into the FlightData object. The issue I am having is (I think) the top of the JSON response has a ton of meta data in it. I don't care about any of it until it says "response" and following that is my data. I think it's trying to parse the meta data into the object, and it's hitting the second catch because the attributes don't line up. The if statement gets me past that, but then it doesn't work properly. It freezes my computer. I have successfully called the API and gotten the data.

In python all I did was

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

and this worked well.

I appreciate any help and I apologize if I did a bad job at explaining. If I let out important information please don't hesitate to tell me.

I tried so many different methods found online and the solution mentioned above is the closest I've gotten. I know that the if statement part works because I output it to a .txt and it did separate the meta data and the response that I actually wanted.


Edit: Here's what the error is when it hits the second catch:

JSON Deserialization Error: The JSON value could not be converted to FlightData. Path: $[0].lat | LineNumber: 0 | BytePositionInLine: 68.


Edit 2: thanks for the speedy replies you guys. Don't know how I forgot to add a sample JSON.

Here is one without the header/metadata (I am honestly not sure what the exact term for it is):

[
   {
      "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"
   }
]

And here is the top part with an entry:

{
   "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"
      }
   ]
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Chris
  • 23
  • 4
  • 2
    Adding a sample `json` would also be nice. – Eldar Aug 15 '23 at 05:35
  • 1
    [File.ReadAllText](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readalltext?view=net-7.0) accepts a file name as parameter. Not sure if you need that. – Orifjon Aug 15 '23 at 05:50
  • 1
    And since you are using getters and setters for `FlightData`, you don't need a constructor with parameters. – Orifjon Aug 15 '23 at 05:54
  • Just out of curiosity - am I understanding right that you are querying some API and then reading a local file based on response? – Guru Stron Aug 15 '23 at 06:12
  • 2
    As for the problem - just a guess - `public string? lat { get; set; }` should be something like `public double? lat { get; set; }`. System.Text.Json is quite picky about correct types being used (i.e. it will not read numbers into strings by default). An the error suggests that there is a type mismatch. P.S. please provide a sample JSON (i.e. a [mre]) – Guru Stron Aug 15 '23 at 06:13
  • Added the JSON. Sorry about that guys. Thanks for the replies. – Chris Aug 15 '23 at 06:42
  • @GuruStron Honestly I am unfamiliar with the terminology. I am calling an API and it is returning a giant response full of text. I do not think it is a file? – Chris Aug 15 '23 at 06:45
  • 2
    @Chris There is not much terminology here. You have `string jsonContent = File.ReadAllText(responseData.ToString());` in your code. `File.ReadAllText` will read text from ... file =) – Guru Stron Aug 15 '23 at 06:46
  • @GuruStron I see. That part of code was another attempt at trying something different. When I use it, it freezes vstudio. When I remove it is when I get the JSON errors. What if I turn it all into a giant string and split/trim based on the commas and brackets/braces? – Chris Aug 15 '23 at 06:48

1 Answers1

2

So assuming that second JSON is the response string you need to fix the types in the destination class (as I wrote in the comments):

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

And then you can introduce the Root one and just use it:

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

And usage:

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

Or just:

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

If you are adamant on the property check then use:

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.

There are a lot of tools allowing to convert JSON into C# types, some are build in in IDEs, or you can use online ones like app.quicktype.io

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • That worked wonderfully. I really appreciate it! I've been on this for hours. If I wanted to read up on what solved the issue, what would I search for? Also, this is unrelated but really quick. Some entries in the JSON are missing certain fields, for example is might be missing the hex, or any combination. Since the properties of FlightData are nullable, will it just leave those blank and fill in what it can with the Json Deserialize? – Chris Aug 15 '23 at 07:18
  • 1
    @Chris was glad to help! Not sure were it is covered in [the docs](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview), TBH. _"will it just leave those blank and fill in what it can with the Json"_ - yes, serializer will use the default value (note that for value types like `int` you might need to use their nullable counterpart - `int?`, search for nullable value types). Also note that you can also use Newtonsoft Json.NET which was de facto standard for quite a lot time. – Guru Stron Aug 15 '23 at 08:09
  • 1
    @Chris As for Newtonsoft's Json.NET usage - it should be a bit more flexible in some conventions (like reading JSON number properties) and I find it's dynamic JSON handling API (with JToken's JObject's etc) a bit more convenient then analogous one via JsonNode in System.Text.Json (basically both can be a bit analogous to what you are doing in python, but I would argue usually they are considered less idiomatic approach then using types as in the answer). – Guru Stron Aug 15 '23 at 12:55