3

I'm trying to deserialize a JSON response from an API and get the NAME of what in this example is "af" (country code of Afghanistan) inside "iso", but the name of the field will be different everytime based on the country code, how can i read the name of the field and retrieve each country code in the api response? thanks!

Example response:

{
   "afghanistan":{
      "iso":{
         "af":1
      },
      "prefix":{
         "+93":1
      },
      "text_en":"Afghanistan",
      "text_ru":"Афганистан",
      "virtual21":{
         "activation":1
      },
      "virtual23":{
         "activation":1
      },
      "virtual29":{
         "activation":1
      },
      "virtual30":{
         "activation":1
      },
      "virtual31":{
         "activation":1
      },
      "virtual32":{
         "activation":1
      },
      "virtual4":{
         "activation":1
      }
   }
}

This goes on for a very large list of countries

this is how i am currently deserializing:

  var response = JsonConvert.DeserializeObject<Dictionary<string, ResponseFields>>(json);

And this is the VS-generated ResponseFields class:

public class ResponseFields
{
    public Iso iso { get; set; }
    public Prefix prefix { get; set; }
    public string text_en { get; set; }
    public string text_ru { get; set; }
    public Virtual21 virtual21 { get; set; }
    public Virtual23 virtual23 { get; set; }
    public Virtual29 virtual29 { get; set; }
    public Virtual30 virtual30 { get; set; }
    public Virtual31 virtual31 { get; set; }
    public Virtual32 virtual32 { get; set; }
    public Virtual4 virtual4 { get; set; }
}

public class Iso
{
    public int af { get; set; }
}

public class Prefix
{
    public int _93 { get; set; }
}

public class Virtual21
{
    public int activation { get; set; }
}

public class Virtual23
{
    public int activation { get; set; }
}

public class Virtual29
{
    public int activation { get; set; }
}

public class Virtual30
{
    public int activation { get; set; }
}

public class Virtual31
{
    public int activation { get; set; }
}

public class Virtual32
{
    public int activation { get; set; }
}

public class Virtual4
{
    public int activation { get; set; }
}

Printing to console as follows:

foreach (var field in response)
      {
        Console.WriteLine($"Name: {(field.Key)} \nCode: {field.Value.iso}");


    }

Produces:

Name: afghanistan
Code: Iso
Name: albania
Code: Iso
Name: algeria
Code: Iso

vs Expected output:

Name: afghanistan
Code: af
Name: albania
Code: al
...

this is the closest post on SO i could manage to pull from google

NineBerry
  • 26,306
  • 3
  • 62
  • 93

4 Answers4

2

you can accomplish your task in one line

var countries = JObject.Parse(json).Properties()
                                       .Select(jo => new
                                       {
                                         name = jo.Name,
                                         code = ((JObject)jo.Value["iso"]).Properties().First().Name
                                       }).ToList();
Serge
  • 40,935
  • 4
  • 18
  • 45
  • Your answer is so simple yet effective that i almost got disappointed at first glance for the useless ammount of head-scratching i put myself into for nothing.. clear sign i still have lots to learn! Thank you so much! – juryvorpopen Nov 17 '22 at 05:20
1

Is iso guaranteed to only have the single property? If so, you can just read the children by index instead of name.

Babak Naffas
  • 12,395
  • 3
  • 34
  • 49
1

You can use Dictionary<string, int> to represent the Iso too (as you do with the root):

public class ResponseFields
{
    // ...
    public Dictionary<string, int> Iso { get; set; }
    // ...
}

And output line can be:

Console.WriteLine($"Name: {(field.Key)} \nCode: {field.Value.Iso.Keys.FirstOrDefault()}");
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • 2
    No idea why this perfectly valid answer would be downvoted. Sometimes the easiest answer is to define a json specific model class. Though you could also solve the problem by defining a custom converter for the `Iso` class. – Jeremy Lakeman Nov 17 '22 at 02:58
1

Solution without using predefined types but navigating the object structure.

string input = @"
    {
       ""afghanistan"":{
          ""iso"":{
             ""af"":1
          },
          ""prefix"":{
             ""+93"":1
          },
          ""text_en"":""Afghanistan"",
          ""text_ru"":""Афганистан"",
          ""virtual21"":{
             ""activation"":1
          },
          ""virtual23"":{
             ""activation"":1
          },
          ""virtual29"":{
             ""activation"":1
          },
          ""virtual30"":{
             ""activation"":1
          },
          ""virtual31"":{
             ""activation"":1
          },
          ""virtual32"":{
             ""activation"":1
          },
          ""virtual4"":{
             ""activation"":1
          }
       },
      ""albania"":{
          ""iso"":{
             ""al"":1
          },
          ""prefix"":{
             ""+98"":1
          },
          ""text_en"":""Albania"",
          ""virtual21"":{
             ""activation"":1
          },
          ""virtual23"":{
             ""activation"":1
          },
          ""virtual29"":{
             ""activation"":1
          },
          ""virtual30"":{
             ""activation"":1
          },
          ""virtual31"":{
             ""activation"":1
          },
          ""virtual32"":{
             ""activation"":1
          },
          ""virtual4"":{
             ""activation"":1
          }
       }
    }
";

JObject o = JObject.Parse(input);

// Foreach property of the root
foreach(JProperty country in o.Children())
{
    // Country name is name of the property
    string countryName = country.Name;

    // Get ISO Node below the country node
    JToken? iso = country.SelectToken("$..iso");

    // Get First property within iso Node
    JProperty? code = iso?.Value<JObject>()?.Properties().First();

    // Get Name of property
    string isoCode = code?.Name ?? "";

    Console.WriteLine($"Name: {countryName} \nCode: {isoCode}");
}
NineBerry
  • 26,306
  • 3
  • 62
  • 93