t313
t313

Reputation: 111

Json.NET: deserialize json arrays

I'm trying to process a JSON structure with Json.NET and encountered some issues:

My classes don't work when a JSON contains an unnamed array.

Json Example:

{
    "graph_property" : [{
            "name" : "calculation_method",
            "value" : "Arithmetic"
        }, {
            "name" : "graph_type",
            "value" : "TIME"
        }
    ],
    "measurement" : [{
            "id" : "9997666",
            "alias" : "Measurement (TxP)[IE]-Home Page - Total Time (seconds)",
            "bucket_data" : [{
                    "name" : "2013-MAR-18 12:00 AM",
                    "id" : 1,
                    "perf_data" : {
                        "value" : "2.244",
                        "unit" : "seconds"
                    },
                    "avail_data" : {
                        "value" : "99.67",
                        "unit" : "percent"
                    },
                    "data_count" : {
                        "value" : "299",
                        "unit" : "#"
                    }
                }
            ],
            "graph_option" : [{
                    "name" : "perfwarning",
                    "value" : "-",
                    "unit" : "seconds"
                }, {
                    "name" : "perfcritical",
                    "value" : "-",
                    "unit" : "seconds"
                }, {
                    "name" : "availwarning",
                    "value" : "-",
                    "unit" : "percent"
                }, {
                    "name" : "availcritical",
                    "value" : "-",
                    "unit" : "percent"
                }, {
                    "name" : "bucketsize",
                    "value" : "86400",
                    "unit" : "seconds"
                }, {
                    "name" : "rows",
                    "value" : "1",
                    "unit" : "#"
                }, {
                    "name" : "pagecomponent",
                    "value" : "Total Time",
                    "unit" : "seconds"
                }, {
                    "name" : "avg_perf",
                    "value" : "2.244",
                    "unit" : "seconds"
                }, {
                    "name" : "avg_avail",
                    "value" : "99.67",
                    "unit" : "percent"
                }, {
                    "name" : "total_datapoint_count",
                    "value" : "300",
                    "unit" : "#"
                }, {}

            ]
        }, {
            "id" : "9997666",
            "alias" : "Measurement (TxP)[IE]-Women - Total Time (seconds)",
            "bucket_data" : [{
                    "name" : "2013-MAR-18 12:00 AM",
                    "id" : 1,
                    "perf_data" : {
                        "value" : "0.979",
                        "unit" : "seconds"
                    },
                    "avail_data" : {
                        "value" : "100.00",
                        "unit" : "percent"
                    },
                    "data_count" : {
                        "value" : "299",
                        "unit" : "#"
                    }
                }
            ],
            "graph_option" : [{
                    "name" : "perfwarning",
                    "value" : "-",
                    "unit" : "seconds"
                }, {
                    "name" : "perfcritical",
                    "value" : "-",
                    "unit" : "seconds"
                }, {
                    "name" : "availwarning",
                    "value" : "-",
                    "unit" : "percent"
                }, {
                    "name" : "availcritical",
                    "value" : "-",
                    "unit" : "percent"
                }, {
                    "name" : "bucketsize",
                    "value" : "86400",
                    "unit" : "seconds"
                }, {
                    "name" : "rows",
                    "value" : "1",
                    "unit" : "#"
                }, {
                    "name" : "pagecomponent",
                    "value" : "Total Time",
                    "unit" : "seconds"
                }, {
                    "name" : "avg_perf",
                    "value" : "0.979",
                    "unit" : "seconds"
                }, {
                    "name" : "avg_avail",
                    "value" : "100.00",
                    "unit" : "percent"
                }, {
                    "name" : "total_datapoint_count",
                    "value" : "299",
                    "unit" : "#"
                }, {}

            ]
        }
    ],
    "link" : {
        "type" : "application/json",
        "href" : "http://api.website.tld?format=json",
        "rel" : "slotmetadata"
    }
}

Class for Json.NET:

using System;
using System.Collections.Generic;

namespace CAKR.Graph
{
    /// <summary>
    /// Description of KN_Graph.
    /// </summary>
    public class GraphProperty
    {
        public string name { get; set; }
        public string value { get; set; }
    }

    public class PerfData
    {
        public string value { get; set; }
        public string unit { get; set; }
    }

    public class AvailData
    {
        public string value { get; set; }
        public string unit { get; set; }
    }

    public class DataCount
    {
        public string value { get; set; }
        public string unit { get; set; }
    }

    public class BucketData
    {
        public string name { get; set; }
        public int id { get; set; }
        public PerfData perf_data { get; set; }
        public AvailData avail_data { get; set; }
        public DataCount data_count { get; set; }
    }

    public class GraphOption
    {
        public string name { get; set; }
        public string value { get; set; }
        public string unit { get; set; }
    }

    public class Measurement
    {
        public string id { get; set; }
        public string alias { get; set; }
        public List<BucketData> bucket_data { get; set; }
        public List<GraphOption> graph_option { get; set; }
    }

    public class Link
    {
        public string type { get; set; }
        public string href { get; set; }
        public string rel { get; set; }
    }

    public class RootObject
    {
        public List<GraphProperty> graph_property { get; set; }
        public List<Measurement> measurement { get; set; }
        public Link link { get; set; }
    }
}

My code:

var myObject = JsonConvert.DeserializeObject<CAKR.Graph.Measurement>(MyJsonString);

I'm not sure why I don't get an object containing the data of the "Measurment" child-array. If I insert named values, it works...

Upvotes: 7

Views: 36392

Answers (4)

Tidy
Tidy

Reputation: 1185

I use a very simple method to Deserialize Json Arrays. Instead of using a ton of public classes with numerous public variables. I just use a Dynamic object and pass json as an object to JSONConvert.DeserializeObject.

This is how it would work. Say I have the following JSON:

string json = { 'Name': 'John Doe', 
  'Address': { 'City': 'Atlanta', 'State': 'GA' }, 
  'Age': 30}

I can pass string json to the JSONConvert.DeserializeObject.

dynamic outputArray = JsonConvert.DeserializeObject(json);

Then using the dynamic item that was just created I can collect Json data like so.

string getName = outputArray.Name //This will return "John Doe"

If you have an array inside your Json you can use

string getCity = outputArray.Address.City; //This will return "Atlanta".

It's very easy to change where you pull data from without having a cluster of public variables... You can still save the values to public variables if you want.

The following is how I use the complete method:

using (var client = new WebClient())
        {
            string json = client.DownloadString(url);
            string output = json.ToString();

            dynamic outputArray = JsonConvert.DeserializeObject(output);

            string _age = outputArray.age;
            string appID = outputArray.data.app_id;

            Debug.Write(outputArray.Something); //Just match value of json        
        }

Upvotes: 0

Raymond
Raymond

Reputation: 91

So I was struggling for quite some time to get this working. However in the end the solution is not so difficult. Hopefully with my response I will help some people.

The solution in my case was

  1. Install JSON.net into your project by using nu.get
  2. Make your JSON object including array's in array's, etc. Be sure that the format of the object is correct! Example...

{"ProductDetail":[

  {
      "ProjectImg"    : "http://placehold.it/400x300",
      "Category"      : "Cars",
      "ProjectTitle"  : "Cars of the future",
      "ProjectDesc"   : "Test project",
      "GenSpecList"   : ["Specs1", "Specs2", "Specs3", "Specs4"],
      "OptionList"    : [{    "OptionNr"  : "1",
                              "Options"   : ["Opt1", "Opt2", "Opt3"]
                          },
                          {   "OptionNr"  : "2",
                              "Options"   : ["Opt1", "Opt2", "Opt3"]
                          },
                          {   "OptionNr"  : "3",
                              "Options"   : ["Opt1", "Opt2", "Opt3"]
                          },
                          {   "OptionNr"  : "4",
                              "Options"   : ["Opt1", "Opt2", "Opt3"]
                          },
                          {   "OptionNr"  : "5",
                              "Options"   : ["Opt1", "Opt2", "Opt3"]
                          },
                          {   "OptionNr"  : "6",
                              "Options"   : ["Opt1", "Opt2", "Opt3"]
                          }
                        ],
      "Articles"      : [{    "tileImg" : "'Images/Project/1.jpg'",
                              "tileTit" : "Title1",
                              "tileArt" : "Article text here..."
                          },
                          {
                              "tileImg" : "'Images/Project/2.jpg'",
                              "tileTit" : "Title2",
                              "tileArt" : "Article text here..."
                          },
                          {
                              "tileImg" : "'Images/Project/3.jpg'",
                              "tileTit" : "Title3",
                              "tileArt" : "Article text here..."
                          },
                          {
                              "tileImg" : "'Images/Project/4.jpg'",
                              "tileTit"   : "Title4",
                              "tileArt"   : "Article text here..."
                          }
                         ]
  }
]
}
  1. Go to json2csharp.com and copy your JSON object into the input box and click on the generate button. Copy the csharp model (this was actually the key to solve my struggle!) generated into your ViewModel.
  2. In my case the primary class of all generated classes by json2csharp.com was the RootObject and looked like below

    public class Article
    {
        public string tileImg { get; set; }
        public string tileTit { get; set; }
        public string tileArt { get; set; }
    }
    
    public class OptionList
    {
        public string OptionNr { get; set; }
        public List<string> Options { get; set; }
    }
    
    public class ProductDetail
    {
        public string ProjectImg { get; set; }
        public string Category { get; set; }
        public string ProjectTitle { get; set; }
        public string ProjectDesc { get; set; }
        public List<string> GenSpecList { get; set; }
        public List<OptionList> OptionList { get; set; }
        public List<Article> Articles { get; set; }
    }
    
    public class RootObject
    {
        public List<ProductDetail> ProductDetail { get; set; }
    }
    
  3. Then use the following code in the controller (just copied the complete file here)

    using Project.Details; //<-- this is my ViewModel namespace name
    using Newtonsoft.Json;
    using System.IO;
    using System.Threading.Tasks;
    
    namespace WebApplication.Controllers
    {
        public class JSONController : Controller
        {
            //
            // GET: /JSON/
            public async Task<ActionResult> Index()
            {
                StreamReader file = new StreamReader("C:\\Users\\YourName\\etc\\File.json");
                String json = await file.ReadToEndAsync();
    
                var Project = JsonConvert.DeserializeObject<RootObject>(json);
    
                return View();
            }
        }
    }
    

Now everything should work fine, array's in array's etc. Hope that you found my solution helpful, note I'm not a die hard programmer so if I miss things on efficency I hope to recieve some tips from you to improve this code...

Best regards, Raymond

Upvotes: 9

Jevgenij Nekrasov
Jevgenij Nekrasov

Reputation: 2760

First of all, you do not really need to name all the properties of your Measurement class exactly as they are presented in MyJsonString. You can use JsonProperty attribute and decorate your class properties.

Another thing is if you want to deserialize part of your MyJsonString and extract only array of Measurement, you should provide correct T type for Deserialize method (in your case it's IEnumerable<Measurement>.

The following code should help:

    dynamic context = JObject.Parse(MyJsonString);
    var myObject = JsonConvert.DeserializeObject<IEnumerable<Measurement>>(context.measurement.ToString());

Upvotes: 2

I4V
I4V

Reputation: 35373

You are almost there. Just use

var myObject = JsonConvert.DeserializeObject<CAKR.Graph.RootObject>(MyJsonString);

instead of

var myObject = JsonConvert.DeserializeObject<CAKR.Graph.Measurement>(MyJsonString);

Upvotes: 4

Related Questions