adamkhor
adamkhor

Reputation: 11

Parsing nested Json data with C#. Could not access inner element consumption

I can parse up to one nested level but I'm unable to figure out how to read the data in the consumption_history. I have been trying different ways to do it but could not get the consumption value.

I can access the miu_id and meter_number but not the array of consumption_list.

Model:

class JsonModel
{
    public class Rootobject
    {
        public string site_id { get; set; }
        public Endpoint[] endpoints { get; set; }
        public Paging paging { get; set; }
    }

    public class Paging
    {
        public int page { get; set; }
        public int limit { get; set; }
        public int total { get; set; }
        public string next { get; set; }
        public object prev { get; set; }
        public string self { get; set; }
    }

    public class Endpoint
    {
        public string miu_id { get; set; }
        public string meter_number { get; set; }
        public Consumption_History[] consumption_hist { get; set; }
    }

    public class Consumption_History
    {
        public DateTime reading_date { get; set; }
        public float consumption { get; set; }
        public float consumption_with_multiplier { get; set; }
    }
}

Program:

static void Main(string[] args)
{
    string json = File.ReadAllText(@"C:\Users\ConsoleApp3\apidataone.json");
    var results = JsonConvert.DeserializeObject<JsonModel.Rootobject>(json);

    JsonModel.Rootobject rootobject = JsonConvert.DeserializeObject<JsonModel.Rootobject>(json);
    rootobject.endpoints = JsonConvert.DeserializeObject<JsonModel.Rootobject>(json).endpoints;

    foreach (JsonModel.Consumption_History ch in rootobject.endpoints)
    {
        Console.WriteLine(ch.consumption);

    }
}

json data:

{
    "site_id":"1",
    "endpoints":
    [{
          "miu_id":"111",
          "meter_number":"88",
          "consumption_history":
          [{
              "reading_date":"2010-02-17T00:00:00", 
              "consumption":1.0, 
              "consumption_with_multiplier":1.0
          }]
    }]
}

Upvotes: 0

Views: 1997

Answers (1)

Matt
Matt

Reputation: 27036

I have simplified the code a bit, instead of reading from a file I have just put the JSON content in place. This code uses Newtonsoft.Json (available via NUGET package), but the way it works is the same for other serializers.

// using Newtonsoft.Json;
void Main()
{
    string json = @"{
        'site_id':'1',
        'endpoints':
        [{
              'miu_id':'111',
              'meter_number':'88',
              'consumption_history':        
              [{
                  'reading_date':'2010-02-17T00:00:00',
                  'consumption':1.0,
                  'consumption_with_multiplier':1.0
              }]
        }]
    }";

    dynamic j = JsonConvert.DeserializeObject<dynamic>(json);
    string site_id = j["site_id"].ToString();
    var endpoints = j["endpoints"];
    foreach (var endpoint in endpoints)
    {
        string miu_id = endpoint["miu_id"];
        miu_id.Dump();
        // ...
        var consumption_histories = endpoint["consumption_history"];
        foreach(var consumption in consumption_histories)
        {
            string reading_date = consumption["reading_date"];
            reading_date.Dump();
            // ...
        } // foreach
    } // foreach
}

Run it in .NET fiddle

Regarding JSON: Whenever there is a [ ... ] in the JSON structure, this means there is an array ({ ... } stands for an object) - and [{ ... }] is an object inside an array; both properties and array elements are separated by comma (,); strings and property names need to be enclosed in single quotes ('...'). Value assignments are done with a colon (:). In JSON, you have either objects, strings or numeric data - these are the only types existing. If you need types, you have to cast properties into the proper types by yourself.

You can access the array either via indexer (like endpoints[i] inside a for loop), or - as I am assuming - you want to go through every element, in that case a foreach loop is easier.

Note: To use it in a for loop (like for (int i = 0; i < endpointsLength; i++) { ... }), you would have to cast endpoints and consumption_histories into an array type (e.g. via var endpoints = (object[])j["endpoints"];) and lose the simplicity dynamic provides (for example, then you would need an indexer to access a property like (endpoints[i])["miu_id"] to be able to get the value of miu_id).

The keyword dynamic simplifies things a lot in this case, this way you can keep the "JavaScript-Style" of the C# code (remember JSON comes from the JavaScript world).

If you want to introduce the classes you have declared in C#, this is also possible. For example:

foreach (Consumption_History consumption in consumption_histories)
{
    string reading_date = consumption.reading_date.ToString();
    reading_date.Dump();
    // ...
} // foreach

You can see, that reading_date is now being accessed as C# property, and because it is of type DateTime, you need to cast it into a string first. This conversion needs to be done before the foreach loop, so you can either change the data type in your class to string to avoid that, or you can use the original code shown above and copy + convert the data into typed versions of your objects (for converting a string into DateTime, you can check this out).

If you rely on the built-in conversion NewtonSoft.JSON provides, you can simply use JsonConvert.DeserializeObject<Rootobject>(json) instead of JsonConvert.DeserializeObject<dynamic>(json). Note that implicit conversions can throw exceptions.

Also, make sure that the property names in C# and in your JSON file match exactly - if not you can decorate them in C# using attributes (see here for a description how to do that) - xdtTransform found that it is not matching for consumption_hist, and mentioned it in the comments.

Upvotes: 3

Related Questions