简体   繁体   中英

Unable to Deserialize data from Nested JSON List

Coming here after learning about C# classes Constructors and ArrayLists so that not to put a completely dumb question here:)

I'm trying to Deserialize below Nested Lists of JSON returned from an API GET call as below:

I've been able to get the value from the empArra (Field: Emp ), but subsequent lists like yearArray , prod and sale are not returning there values.

Can you please look into the below code that where is it doing wrong?

JSON

 [
    {
        "employee":"156718100",
        "availability":[
            {
                "year":2018,
                "sales":{
                    "availability":"Maybe",
                    "reason":""
                },
                "prod":{
                    "availability":"Maybe",
                    "reason":""
                }
            },
            {
                "year":2019,
                "sales":{
                    "availability":"Maybe",
                    "reason":""
                },
                "prod":{
                    "availability":"Maybe",
                    "reason":""
                }
            },
            {
                "year":2020,
                "sales":{
                    "availability":"Maybe",
                    "reason":""
                },
                "top":{
                    "availability":"Maybe",
                    "reason":""
                }
            },
            {
                "year":2021,
                "sales":{
                    "availability":"Maybe",
                    "reason":""
                },
                "prod":{
                    "availability":"Maybe",
                    "reason":""
                }
            }
        ]
    }
]

Classes

public class sale
{
    public string SaleAvailability { get; set; }
    public string SaleReason { get; set; }
    
    public sale(string pSaleAvailability, string pSaleReason)
    {
        this.SaleAvailability = pSaleAvailability;
        this.SaleReason = pSaleReason;
    }
    
    public override string ToString()
    {
        return string.Format("{0} {1}", SaleAvailability, SaleReason);
    }
}

public class prod
{
    public string ProdAvailability { get; set; }
    public string ProdReason { get; set; }
    
    public prod(string pProdAvailability, string pProdReason)
    {
        this.ProdAvailability = pProdAvailability;
        this.ProdReason = pProdReason;
    }
    
    public override string ToString()
    {
        return string.Format("{0} {1}", ProdAvailability, ProdReason);
    }
}

public class yearArray
{
    public int Year { get; set; }

    public yearArray(int pYear)
    {
        this.Year = pYear;
    }

    List<sale> Sale { get; set; } = new List<sale>();
    List<prod> Prod { get; set; } = new List<prod>();
}

public class rootAvailability
{
    public List<yearArray> YearArray { get; set; } = new List<yearArray>();
}

public class empArray
{
    public string Emp { get; set; }
    public List<rootAvailability> RootAvailability { get; set; } = new List<rootAvailability>();
}

public class rootArray
{
    public List<empArray> EmpArrays { get; set; } = new List<empArray>();

}

main() method

(After getting the response from API)

IRestResponse response = client.Execute<rootArray>(request);
    
//Console.WriteLine(response.Content);

List<rootArray> rootArrays = JsonConvert.DeserializeObject<List<rootArray>>(response.Content);

List<empArray> empArrays = JsonConvert.DeserializeObject<List<empArray>>(response.Content);

List<rootAvailability> rootAvailabilities = JsonConvert.DeserializeObject<List<rootAvailability>>(response.Content);

List<yearArray> yearArrays = JsonConvert.DeserializeObject<List<yearArray>>(response.Content);

List<sale> clsSale = JsonConvert.DeserializeObject<List<sale>>(response.Content);

List<prod> clsProd = JsonConvert.DeserializeObject<List<prod>>(response.Content);


foreach (var rootitem in rootArrays)
{
    foreach (var emparrayitem in empArrays)
    {
        Console.WriteLine("NSN: " + emparrayitem.Emp);
        
        foreach (var rootavailabbilitiesitem in rootAvailabilities)
        {
            
            foreach (var yearArrayItem in yearArrays)
            {
                
                Console.WriteLine("Year: " + yearArrayItem.Year);

                foreach (var saleItem in clsSale)
                {

                    Console.WriteLine("SaleAvailability: " + saleItem.SaleAvailability);
                    Console.WriteLine("SaleReason: " + saleItem.SaleReason);
                }
                foreach (var prodItem in clsProd)
                {

                    Console.WriteLine("SaleAvailability: " + prodItem.ProdAvailability);
                    Console.WriteLine("SaleReason: " + prodItem.ProdReason);
                }
            }
        }
    }
}

Results

Emp: 159252663
Year: 0
SaleAvailability:
SaleReason:
SaleAvailability:
SaleReason:

You have two problems with your approach:

  1. You want to deserialize the same source over and over again ( response.Content ) for different class instances. It can be deserialized into one object type: your top level entity.
  1. Your data model does not reflect your data. For example YearArray should have a single Prod and Sale property not a list of them.

You have several options how to fix it. Let me share with you the two most common ones:

With proper naming

Your object model should look like this:

public class Sale
{
    public string Availability { get; set; }
    public string Reason { get; set; }
}

public class Prod
{
    public string Availability { get; set; }
    public string Reason { get; set; }
}

public class MidLevel
{
    public int Year { get; set; }
    public Sale Sales { get; set; }
    public Prod Top { get; set; }
}

public class TopLevel
{
    public string Employee { get; set; }
    public List<MidLevel> Availability { get; set; } = new List<MidLevel>();
}

Then all you need to do is to call the following command:

var result = JsonConvert.DeserializeObject<TopLevel[]>(json);

Now, your result will be populated with all the data.

With JsonProperty

If you don't want to use the same names in your domain model which is used in the json then you can define the mapping between these two worlds via JsonProperty attributes.

Now your domain model can look like this:

public class SalesInformation
{
    [JsonProperty(PropertyName = "availability")]
    public string Avail { get; set; }
    [JsonProperty(PropertyName = "reason")]
    public string Reasoning { get; set; }
}

public class ProdInformation
{
    [JsonProperty(PropertyName = "availability")]
    public string Availability { get; set; }
    [JsonProperty(PropertyName = "reason")]
    public string Reasoning { get; set; }
}

public class MidLevel
{
    [JsonProperty(PropertyName = "year")]
    public int AvailYear { get; set; }
    [JsonProperty(PropertyName = "sales")]
    public SalesInformation SalesInfos { get; set; }
    [JsonProperty(PropertyName = "top")]
    public ProdInformation ProdInfos { get; set; }
}

public class TopLevel
{
    [JsonProperty(PropertyName = "employee")]
    public string Emp { get; set; }
    [JsonProperty(PropertyName = "availability")]
    public List<MidLevel> Availabilities { get; set; } = new List<MidLevel>();
}

The usage would be exactly the same:

var result = JsonConvert.DeserializeObject<TopLevel[]>(json);

UPDATE : How to display data

To represent hierarchy in a console application can be achieved in may ways. Here I will use indentation. I've introduced the following tiny helper method:

public static void WriteWithIndent(int level, string message) => Console.WriteLine("".PadLeft(level * 2) + message);

With this in hand the data visualization could be achieved in the following way:

var result = JsonConvert.DeserializeObject<TopLevel[]>(json);
foreach (var topLevel in result)
{
    Console.WriteLine($"Employee: {topLevel.Emp}");
    foreach (var midLevel in topLevel.Availabilities)
    {
        WriteWithIndent(1, $"Year: {midLevel.AvailYear}");
        WriteWithIndent(1, "Sales:");
        WriteWithIndent(2, $"Avail: {midLevel.SalesInfos.Avail}");
        WriteWithIndent(2, $"Reason: {midLevel.SalesInfos.Reasoning}");
        WriteWithIndent(1, "Top:");
        WriteWithIndent(2, $"Avail: {midLevel.ProdInfos.Avail}");
        WriteWithIndent(2, $"Reason: {midLevel.ProdInfos.Reasoning}");
    }
}

The printed output will look like this:

Employee: 156718100
  Year: 2018
  Sales:
    Avail: Maybe
    Reason:
  Top:
    Avail: Maybe
    Reason:
  Year: 2019
  Sales:
    Avail: Maybe
    Reason:
  Top:
    Avail: Maybe
    Reason:
  Year: 2020
  Sales:
    Avail: Maybe
    Reason:
  Top:
    Avail: Maybe
    Reason:
  Year: 2021
  Sales:
    Avail: Maybe
    Reason:
  Top:
    Avail: Maybe
    Reason:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM