2

I'm trying to do a function that returns the sum of values that I get on a Json file. So for that I've created a model to desarialize the Json file it is like this: using System.Collections.Generic;

namespace ServerMonitoringApi.Controllers.CpuUsage.Model
{
    public class Metric
    {
        public string __name__ { get; set; }
        public string core { get; set; }
        public string instance { get; set; }
        public string job { get; set; }
    }

    public class Result
    {
        public Metric metric { get; set; }
        public IList<object> value { get; set; }
    }

    public class Data
    {
        public string resultType { get; set; }
        public IList<Result> result { get; set; }
    }

    public class CpuUsageResponse
    {
        public string status { get; set; }
        public Data data { get; set; }
    }
}

It works fine for deserializing etc.. I've tested it.

In my controller I have this function that return the sum and cause the problem:

[HttpGet("Number/{instance}&{port}")]
public async Task<double> GetNumCpuUsagePerInstance(string instance, string port)
{
    string perInstanceLink = MetricApiLink + "{instance=\"" + instance + ":" + port + "\"}";
    string idleModeLink = MetricApiLink +
        "{mode=\"idle\",instance=\"" + instance + ":" + port + "\"}";
    dynamic dataGetAll;
    dynamic dataGetIdle;
    using (var httpClient = new HttpClient())
    {
        using (var response = await httpClient
            .GetAsync(perInstanceLink))
        {
            string apiResponse = await response.Content.ReadAsStringAsync();
            dataGetAll = JsonConvert.DeserializeObject<CpuUsageResponse>(apiResponse);
        }
    }
    using (var httpClient = new HttpClient())
    {
        using (var response = await httpClient
            .GetAsync(idleModeLink))
        {
            string apiResponse = await response.Content.ReadAsStringAsync();
            dataGetIdle = JsonConvert.DeserializeObject<CpuUsageResponse>(apiResponse);
        }
    }

    double sum1 = 0;
    double sum2 = 0;
    NumberFormatInfo provider = new NumberFormatInfo();
    provider.NumberGroupSeparator = ".";

    foreach(CpuUsageResponse x in dataGetAll)
    {
        sum1 += Convert.ToDouble(x.data.result[1].value[1], provider);
    }

    return sum1;
}

I have tested every function they work fine but When I try to do "Foreach" it doesnt work and gives me this error msg :

RuntimeBinderException: Cannot implicitly convert type 'ServerMonitoringApi.Controllers.CpuUsage.Model.CpuUsageResponse' to 'System.Collections.IEnumerable'. An explicit conversion exists (are you missing a cast?) And say that the error is in the line 123 which is:

    NumberFormatInfo provider = new NumberFormatInfo();
    provider.NumberGroupSeparator = ".";

    /*here(line 123)->*/ foreach(CpuUsageResponse x in dataGetAll)
    {
        sum1 += Convert.ToDouble(dataGetAll.data.result[1].value[1], provider);
    }

My Json File is like:

{
    "status": "success",
    "data": {
        "resultType": "vector",
        "result": [
            {
                "metric": {
                    "__name__": "wmi_cpu_time_total",
                    "core": "0,0",
                    "instance": "192.168.1.101:9182",
                    "job": "Server-monitoring-Api"
                },
                "value": [
                    1583873150.877,
                    "95595.25"
                ]
            },
            {
                "metric": {
                    "__name__": "wmi_cpu_time_total",
                    "core": "0,1",
                    "instance": "192.168.1.101:9182",
                    "job": "Server-monitoring-Api"
                },
                "value": [
                    1583873150.877,
                    "103647.703125"
                ]
            },
            {
                "metric": {
                    "__name__": "wmi_cpu_time_total",
                    "core": "0,2",
                    "instance": "192.168.1.101:9182",
                    "job": "Server-monitoring-Api"
                },
                "value": [
                    1583873150.877,
                    "94185.015625"
                ]
            },
            {
                "metric": {
                    "__name__": "wmi_cpu_time_total",
                    "core": "0,3",
                    "instance": "192.168.1.101:9182",
                    "job": "Server-monitoring-Api"
                },
                "value": [
                    1583873150.877,
                    "102109.203125"
                ]
            },
            {
                "metric": {
                    "__name__": "wmi_cpu_time_total",
                    "core": "0,4",
                    "instance": "192.168.1.101:9182",
                    "job": "Server-monitoring-Api"
                },
                "value": [
                    1583873150.877,
                    "96709.59375"
                ]
            },
            {
                "metric": {
                    "__name__": "wmi_cpu_time_total",
                    "core": "0,5",
                    "instance": "192.168.1.101:9182",
                    "job": "Server-monitoring-Api"
                },
                "value": [
                    1583873150.877,
                    "102046.5625"
                ]
            },
            {
                "metric": {
                    "__name__": "wmi_cpu_time_total",
                    "core": "0,6",
                    "instance": "192.168.1.101:9182",
                    "job": "Server-monitoring-Api"
                },
                "value": [
                    1583873150.877,
                    "98963.453125"
                ]
            },
            {
                "metric": {
                    "__name__": "wmi_cpu_time_total",
                    "core": "0,7",
                    "instance": "192.168.1.101:9182",
                    "job": "Server-monitoring-Api"
                },
                "value": [
                    1583873150.877,
                    "89011.53125"
                ]
            }
        ]
    }
}

I'm trying to sum the values that have the index 1 i.e value[1] and return it

dbc
  • 104,963
  • 20
  • 228
  • 340
Hasagiii
  • 345
  • 1
  • 3
  • 21

1 Answers1

2

Your JSON consists of a single root object containing a data.result [] array:

{
   "status":"success",
   "data":{
      "resultType":"vector",
      "result":[/* Results */]
   }
}

Where each result looks like this:

{
    "metric": {
        "__name__": "wmi_cpu_time_total",
        "core": "0,0",
        "instance": "192.168.1.101:9182",
        "job": "Server-monitoring-Api"
    },
    "value": [
        1583851813.666,
        "79186.65625"
    ]
}

As such, looping through the outermost JSON container as you try to do here

foreach(CpuUsageResponse x in dataGetAll)
{
    sum1 += Convert.ToDouble(x.data.result[1].value[1], provider);
}

Doesn't really make sense. If the outermost JSON container had been array, that would have been a sensible thing to do.

Instead, if you want to add up all the data.result[*].value[1] values into a double result, you can do so as follows:

CpuUsageResponse dataGetAll;

// Download the apiResponse JSON string (code omitted)

dataGetAll = JsonConvert.DeserializeObject<CpuUsageResponse>(apiResponse);

var sum = dataGetAll.data.result
    .Select(r => r.value[1])
    .Select(s => Convert.ToDouble(s, NumberFormatInfo.InvariantInfo))
    .Sum();

Demo fiddle here.

Notes:

  • Do not declare dataGetAll as dynamic. By doing so you eliminate all compile-time checking for code correctness, and replace it with runtime errors like the RuntimeBinderException exception shown in your question. Since you created a c# data model you should use it, and if you had, you would have received a much clearer compilation error:

    CpuUsageResponse dataGetAll;
    
    dataGetAll = JsonConvert.DeserializeObject<CpuUsageResponse>(apiResponse);
    
    double sum1 = 0;
    NumberFormatInfo provider = new NumberFormatInfo();
    provider.NumberGroupSeparator = ".";
    
    //Compilation error (line 63, col 43): foreach statement cannot operate on variables of type 'CpuUsageResponse' because 'CpuUsageResponse' does not contain a public instance definition for 'GetEnumerator'
    foreach(CpuUsageResponse x in dataGetAll)
    {
        sum1 += Convert.ToDouble(x.data.result[1].value[1], provider);
    }   
    

    Demo fiddle #2 here.

    See When should one use dynamic keyword in c# 4.0?, Is the use of dynamic considered a bad practice? and What is the 'dynamic' type in C# 4.0 used for? for further discussion of when to use, and not to use, dynamic.

  • Your CpuUsageResponse data model looks correct for the JSON provided.

  • Rather than constructing your own, custom NumberFormatInfo, you can just use NumberFormatInfo.InvariantInfo.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Thanks a lot, it worked I'm not used to work with the lambda expressions, but they always save lifes :) – Hasagiii Mar 10 '20 at 21:41