2

I'm trying to play with the Prometheus Monitoring tool and it's providing the following response in some metrics I'm querying:

{
    "status": "success",
    "data": {
        "resultType": "matrix",
        "result": [{
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "guest"
            },
            "values": [
                [1518164248.959, "0"],
                [1518164263.959, "0"],
                [1518164278.959, "0"],
                [1518164293.959, "0"],
                [1518164308.959, "0"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "guest_nice"
            },
            "values": [
                [1518164248.959, "0"],
                [1518164263.959, "0"],
                [1518164278.959, "0"],
                [1518164293.959, "0"],
                [1518164308.959, "0"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "idle"
            },
            "values": [
                [1518164248.959, "11969.01"],
                [1518164263.959, "11983.93"],
                [1518164278.959, "11998.83"],
                [1518164293.959, "12013.73"],
                [1518164308.959, "12028.64"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "iowait"
            },
            "values": [
                [1518164248.959, "29.2"],
                [1518164263.959, "29.21"],
                [1518164278.959, "29.21"],
                [1518164293.959, "29.22"],
                [1518164308.959, "29.23"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "irq"
            },
            "values": [
                [1518164248.959, "0"],
                [1518164263.959, "0"],
                [1518164278.959, "0"],
                [1518164293.959, "0"],
                [1518164308.959, "0"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "nice"
            },
            "values": [
                [1518164248.959, "2.49"],
                [1518164263.959, "2.49"],
                [1518164278.959, "2.49"],
                [1518164293.959, "2.49"],
                [1518164308.959, "2.49"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "softirq"
            },
            "values": [
                [1518164248.959, "1.47"],
                [1518164263.959, "1.48"],
                [1518164278.959, "1.48"],
                [1518164293.959, "1.48"],
                [1518164308.959, "1.48"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "steal"
            },
            "values": [
                [1518164248.959, "0"],
                [1518164263.959, "0"],
                [1518164278.959, "0"],
                [1518164293.959, "0"],
                [1518164308.959, "0"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "system"
            },
            "values": [
                [1518164248.959, "34.36"],
                [1518164263.959, "34.38"],
                [1518164278.959, "34.41"],
                [1518164293.959, "34.44"],
                [1518164308.959, "34.46"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu0",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "user"
            },
            "values": [
                [1518164248.959, "40.93"],
                [1518164263.959, "40.96"],
                [1518164278.959, "41"],
                [1518164293.959, "41.05"],
                [1518164308.959, "41.08"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "guest"
            },
            "values": [
                [1518164248.959, "0"],
                [1518164263.959, "0"],
                [1518164278.959, "0"],
                [1518164293.959, "0"],
                [1518164308.959, "0"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "guest_nice"
            },
            "values": [
                [1518164248.959, "0"],
                [1518164263.959, "0"],
                [1518164278.959, "0"],
                [1518164293.959, "0"],
                [1518164308.959, "0"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "idle"
            },
            "values": [
                [1518164248.959, "11980.39"],
                [1518164263.959, "11995.32"],
                [1518164278.959, "12010.24"],
                [1518164293.959, "12025.17"],
                [1518164308.959, "12040.07"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "iowait"
            },
            "values": [
                [1518164248.959, "30.36"],
                [1518164263.959, "30.36"],
                [1518164278.959, "30.36"],
                [1518164293.959, "30.36"],
                [1518164308.959, "30.36"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "irq"
            },
            "values": [
                [1518164248.959, "0"],
                [1518164263.959, "0"],
                [1518164278.959, "0"],
                [1518164293.959, "0"],
                [1518164308.959, "0"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "nice"
            },
            "values": [
                [1518164248.959, "0.02"],
                [1518164263.959, "0.02"],
                [1518164278.959, "0.02"],
                [1518164293.959, "0.02"],
                [1518164308.959, "0.02"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "softirq"
            },
            "values": [
                [1518164248.959, "0.64"],
                [1518164263.959, "0.64"],
                [1518164278.959, "0.64"],
                [1518164293.959, "0.64"],
                [1518164308.959, "0.64"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "steal"
            },
            "values": [
                [1518164248.959, "0"],
                [1518164263.959, "0"],
                [1518164278.959, "0"],
                [1518164293.959, "0"],
                [1518164308.959, "0"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "system"
            },
            "values": [
                [1518164248.959, "22.17"],
                [1518164263.959, "22.18"],
                [1518164278.959, "22.2"],
                [1518164293.959, "22.22"],
                [1518164308.959, "22.24"]
            ]
        }, {
            "metric": {
                "__name__": "node_cpu",
                "cpu": "cpu1",
                "instance": "localhost:9100",
                "job": "node",
                "mode": "user"
            },
            "values": [
                [1518164248.959, "34.07"],
                [1518164263.959, "34.09"],
                [1518164278.959, "34.1"],
                [1518164293.959, "34.11"],
                [1518164308.959, "34.16"]
            ]
        }]
    }
}

I'm trying to deserialize this to an object and this is working except for this section:

"values": [
    [1518164248.959, "0"],
    [1518164263.959, "0"],
    [1518164278.959, "0"],
    [1518164293.959, "0"],
    [1518164308.959, "0"]
]

It's deserializing, but value is always null.

I figure this is because I'm incorrectly interpreting this data through the object. This is what I've got:

public class CpuMetrics
{
    public string status { get; set; }
    public CpuData data { get; set; }
}

public class CpuData
{
    public string resultType { get; set; }
    public List<Result> result { get; set; }
}

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

public class Metric
{
    public string __name__ { get; set; }
    public string cpu { get; set; }
    public string instance { get; set; }
    public string job { get; set; }
    public string mode { get; set; }
}

Example of the object returning null:

Visual studio Screenshot

Since this data is always consistent, I figured I wouldn't have to create a custom JsonConverter in this case. Am I wrong? Should I create my own extension of the JsonConverter to manage these specific fields?

Dandy
  • 1,466
  • 16
  • 31
  • @ManfredRadlwimmer I have tried a few different ways but nothing seems to be working. I've attached an example of my VS with the results with the `List` changes. – Dandy Feb 09 '18 at 08:03
  • 1
    That screenshot yields other results than the code you have shown. Please post the real code, since I can't reproduce it. – Patrick Hofman Feb 09 '18 at 08:07
  • 1
    Anyone who want's to tinker around with it, [here is a Fiddle of the existing code](https://dotnetfiddle.net/Widget/Wf4gv9). – Manfred Radlwimmer Feb 09 '18 at 08:13
  • @Dandy you could treat the data as a `Tuple` and use this approach: [Deserialize JSON array of arrays into List of Tuples](https://stackoverflow.com/questions/45488785/deserialize-json-array-of-arrays-into-list-of-tuples-using-newtonsoft) – Manfred Radlwimmer Feb 09 '18 at 08:20
  • 1
    @ManfredRadlwimmer in your fiddle you need to deserialize to `CpuMetrics` instead of `Result`, it then works normally and correctly parses the value. As for the question, @Dandy is the variable null on all `Result` objects or just that one? Are you sure the json is what you think it is? – RMH Feb 09 '18 at 08:26
  • @RMH Oh, right ... well that's embarrassing (fixed). – Manfred Radlwimmer Feb 09 '18 at 08:30

2 Answers2

2

Here is a working version. I used https://app.quicktype.io/#l=cs&r=json2csharp

To generate the object model. Note the type handling in the Value deserialization.

using System;
using System.Collections.Generic;
using System.Net;
using Newtonsoft.Json;

namespace ConsoleApp2
{
    public class Program
    {
        public static void Main()
        {
            string json =
                "{ \"status\": \"success\", \"data\": { \"resultType\": \"vector\", \"result\": [ { \"metric\": { \"__name__\": \"node_cpu\", \"cpu\": \"cpu0\", \"instance\": \"localhost:9100\", \"job\": \"node\", \"mode\": \"guest\" }, \"value\": [ 1518159211.958, \"0\" ] }, { \"metric\": { \"__name__\": \"node_cpu\", \"cpu\": \"cpu0\", \"instance\": \"localhost:9100\", \"job\": \"node\", \"mode\": \"guest_nice\" }, \"value\": [ 1518159211.958, \"0\" ] } ] } }";
            var data = VerificationResponse.FromJson(json);
            Console.WriteLine(data);
        }
    }


    public partial class VerificationResponse
    {
        [JsonProperty("status")]
        public string Status { get; set; }

        [JsonProperty("data")]
        public Data Data { get; set; }
    }

    public partial class Data
    {
        [JsonProperty("resultType")]
        public string ResultType { get; set; }

        [JsonProperty("result")]
        public List<Result> Result { get; set; }
    }

    public partial class Result
    {
        [JsonProperty("metric")]
        public Metric Metric { get; set; }

        [JsonProperty("value")]
        public List<Value> Value { get; set; }
    }

    public partial class Metric
    {
        [JsonProperty("__name__")]
        public string Name { get; set; }

        [JsonProperty("cpu")]
        public string Cpu { get; set; }

        [JsonProperty("instance")]
        public string Instance { get; set; }

        [JsonProperty("job")]
        public string Job { get; set; }

        [JsonProperty("mode")]
        public string Mode { get; set; }
    }

    public partial struct Value
    {
        public double? Double;
        public string String;
    }

    public partial class VerificationResponse
    {
        public static VerificationResponse FromJson(string json) => JsonConvert.DeserializeObject<VerificationResponse>(
            json, Converter.Settings);
    }

    public partial struct Value
    {
        public Value(JsonReader reader, JsonSerializer serializer)
        {
            Double = null;
            String = null;

            switch (reader.TokenType)
            {
                case JsonToken.Integer:
                case JsonToken.Float:
                    Double = serializer.Deserialize<double>(reader);
                    return;
                case JsonToken.String:
                case JsonToken.Date:
                    String = serializer.Deserialize<string>(reader);
                    return;
            }
            throw new Exception("Cannot convert Value");
        }

        public void WriteJson(JsonWriter writer, JsonSerializer serializer)
        {
            if (Double != null)
            {
                serializer.Serialize(writer, Double);
                return;
            }
            if (String != null)
            {
                serializer.Serialize(writer, String);
                return;
            }
            throw new Exception("Union must not be null");
        }
    }

    public static class Serialize
    {
        public static string ToJson(this VerificationResponse self) => JsonConvert.SerializeObject(self,
            Converter.Settings);
    }

    public class Converter : JsonConverter
    {
        public override bool CanConvert(Type t) => t == typeof(Value);

        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
        {
            if (t == typeof(Value))
                return new Value(reader, serializer);
            throw new Exception("Unknown type");
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var t = value.GetType();
            if (t == typeof(Value))
            {
                ((Value) value).WriteJson(writer, serializer);
                return;
            }
            throw new Exception("Unknown type");
        }

        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters = {new Converter()},
        };
    }
}
Matt Evans
  • 7,113
  • 7
  • 32
  • 64
  • I've had to update my original question although I have a feeling using this tool will resolve my problem. Reviewing now. Thank you. – Dandy Feb 09 '18 at 08:40
  • 1
    I couldn't get this to work as it kept failing at the `FromJson()`. I was in the process of trying to debug the error but it turns out the problem was a simple typo in my Class object. – Dandy Feb 09 '18 at 08:51
1

After your last update I was able to reproduce the error. Basically you have a typo in your code. It's not value but values.

public class Result
{
    public Metric metric { get; set; }
    public List<object> values { get; set; }
}
derloopkat
  • 6,232
  • 16
  • 38
  • 45
  • I actually want to cry at the fact this was due to a typo. I've resolved it with this. Thank you. This resolved it for me without the painful effort of creating my own JsonConvert extension. – Dandy Feb 09 '18 at 08:50
  • @Dandy If you are using Visual Studio, there's a nice `Edit->Paste Special ->Paste Json As Classes` Option... to avoid typos and such. – Faheem Feb 09 '18 at 09:24