Question

I am dealing with JSON for the first time and getting data from OpenTSDB. I've created a c# class to deserialize the JSON to but I am getting the error 'Cannot deserialize the current JSON array' as described below.

My c# code to get JSON:

 var request = WebRequest.Create("http://localhost:4242/api/query?start=2013/08/21-12:00:00&end=2013/08/22-12:00:00&m=sum:tcollector.collector.lines_sent&o=&yrange=%5B0:%5D&wxh=924x773");
        request.ContentType = "application/json; charset=utf-8";
        string text;
        try
        {
            var response = (HttpWebResponse) request.GetResponse();
            using (var sr = new StreamReader(response.GetResponseStream()))
            {
                text = sr.ReadToEnd();
            }

            uxResponse.Text = text;

            OpenTSDBResponse myObject = (OpenTSDBResponse)Newtonsoft.Json.JsonConvert.DeserializeObject(text, typeof(OpenTSDBResponse));

            var variable = Newtonsoft.Json.JsonConvert.DeserializeObject(text);

            //var tester = myObject;
        }
        catch (Exception ex)
        {
            uxResponse.Text = GetFullExceptionMessage(ex);
        }

The JSON I am receiving from the above code (i.e. the 'text' variable):

[{
"metric":"tcollector.collector.lines_sent",
"tags":
    {
    "host":"ubuntu1"
    },
"aggregateTags":["collector"],
"dps":
    {
    "1377050434":1271779.0,
    "1377050494":1272073.0,
    "1377050554":1272502.0,
    "1377050614":1273632.0,
    "1377050674":1273867.0
    }
}]

My c# classes

 internal class OpenTSDBResponse
    {
        [JsonProperty("metric")]
        public string Metric { get; set; }

        [JsonProperty("tags")]
        public Tags Tags { get; set; }

        [JsonProperty("aggregateTags")]
        public string[] AggregateTags { get; set; }

        [JsonProperty("dps")]
        public List<TimeValue> TimeValues { get; set; }
    }

internal class Tags
{

    [JsonProperty("host")]
    public string Host { get; set; }
}

internal class TimeValue
{
    [JsonProperty("Time")]
    public double Time { get; set; }

    [JsonProperty("Value")]
    public double Value { get; set; }
}

The error when deserializing object:

Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'MyNamespace.OpenTSDBResponse' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.Path '', line 1, position 1.

Additional Information

I used the codeproject deserialize JSON project to create my basic classes, but it created a new c# property for each '"1377050434":1271779.0,' so I updated to use my TimeValue class. http://www.codeproject.com/Tips/79435/Deserialize-JSON-with-C

Question:

How do I get this into an appropriate c# class structure?

Additional Information in response to users comments below:

  • bjaminn's comment: I believe the JSON you are receiving is an array. The exception is trying to say you are converting an object[] to OpenTSDBResponse when you really want OpenTSDBResponse[]. Another way to debug this would be to look at the variable variable and see what type it is in the debugger. Of course the line that throws the exception would need to be commented out.

    Outcome: I modified the deserialize like this

    OpenTSDBResponse[] myObject = (OpenTSDBResponse[])Newtonsoft.Json.JsonConvert.DeserializeObject(text, typeof(OpenTSDBResponse[]));

    but received the following error when I ran it:

    Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[MyNamespace.OpenTSDBResponseJsonTypes.TimeValue]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.Path '[0].dps.1377050434', line 1, position 121.

Additional Notes on working solution for other new to JSON I have added another property to my class for the Dictionary as it's really "unix-time-stamp-data","Value". This allows me to work in c# with datetime/values. There may be a better way for casting but this works an doesn't cause any noticeable performance issues for my scenario.

    [JsonProperty("dps")]
    public Dictionary<string, double> TimeValues { get; set; }

    public List<TimeValue> DataPoints
    {
        get
        {
            List<TimeValue> times = new List<TimeValue>();
            DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);

            foreach (var item in TimeValues)
            {
                times.Add(new TimeValue
                {
                    Time = dtDateTime.AddSeconds(double.Parse(item.Key)).ToLocalTime(),
                    Value = item.Value
                });
            }

            return times;
        }
    }
Was it helpful?

Solution

I believe the JSON you are receiving is an array. The exception is trying to say you are converting an object[] to OpenTSDBResponse when you really want OpenTSDBResponse[].

Another way to debug this would be to look at the variable variable and see what type it is in the debugger. Of course the line that throws the exception would need to be commented out.

Tackling new error It looks like DPS is not a proper JSON array. You could parse it to a dictionary since it looks like the keys will be different in each JSON call.

JSON convert dictionary to a list of key value pairs

New class:

    internal class OpenTSDBResponse
    {
        [JsonProperty("metric")]
        public string Metric { get; set; }

        [JsonProperty("tags")]
        public Tags Tags { get; set; }

        [JsonProperty("aggregateTags")]
        public string[] AggregateTags { get; set; }

        [JsonProperty("dps")]
        public Dictionary<string,double> TimeValues { get; set; }
    }

OTHER TIPS

You can such so modify your Json Data and your C# Code,for example
    [{
    "metric":"tcollector.collector.lines_sent",
    "tags":
    {
    "host":"ubuntu1"
    },
    "aggregateTags":["collector"],
    "dps":
    [
    {"Time":"1377050434","Value":1271779.0},
    {"Time":"1377050554","Value":1272502.0}        
    ]
    }]
c# Code:        
You provide the data is an Array,so when you deserialize the string,you must such so use generic format of deserializeobject

    object obj=Newtonsoft.Json.JsonConvert.DeserializeObject<List<OpenTSDBResponse>>(json.ToString());
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top