c# nested json to object (current with json.net) - c#

i am new to c# and json.net.
I have a json configuration file and try to parse it to objects. But how can i map the relations correctly in the objects?
Currently the property in project loop is null.
And can the objects map "automatically" without mapping each property name/value?
I can also change the json!
configuration.json:
{
"debug": true,
"log": "database",
"projects": [
{
"name": "Name 1",
"showInfo": false,
"ranges": [
[
5,
6
],
[
9,
10
],
[
15,
20
]
],
"additional": [
{
"name": "subName 1",
"parameter": "ID"
},
{
"name": "subName 2",
"parameter": "ID2"
}
]
},
{
"name": "Name 2",
"showInfo": false,
"ranges": [
[
99,
100
]
]
},
{
"name": "Name 3",
"showInfo": false,
"ranges": [
[
44,
45
]
]
},
{
"name": "Name 4",
"showInfo": false,
"ranges": [
[
12,
14
]
],
"additional": [
{
"name": "subName xy",
"parameter": "ID"
}
]
}
]
}
my try to parse:
Configuration configuration = new Configuration();
JObject jObject = JObject.Parse(File.ReadAllText(filePath));
if (jObject.ContainsKey("debug"))
{
configuration.Debug = (bool) jObject["debug"];
}
if (jObject.ContainsKey("log"))
{
configuration.Log = (string) jObject["log"];
}
//loop projects
JToken projects = jObject["projects"];
foreach (JToken child in projects.Children())
{
var property = child as JProperty;
if (property != null)
{
var test = property.Name;
var test2 = property.Value;
}
}
the objects:
public class Configuration
{
public bool Debug { get; set; } = false;
public string Log { get; set; }
// this is propably wrong
public Dictionary<string, Dictionary<string, Project>> Projects { get; set; }
}
public class Project
{
public string name { get; set; }
public bool showInfo{ get; set; }
// wrong?
public int[,] ranges { get; set; }
// wrong?
public Additional[] Additional{ get; set; }
}
public class Additional
{
public string Name{ get; set; }
public string Parameter { get; set; }
}

You don't need a JObject at all json files can be desterilized to objects it's much more efficient because you don't create unneeded objects.
You Config class is just wrong
public class Configuration
{
public bool Debug { get; set; }
public string Log { get; set; }
public Project[] Projects { get; set; }
}
public class Project
{
public string Name { get; set; }
public bool ShowInfo { get; set; }
public int[][] Ranges { get; set; }
public Additional[] Additional { get; set; }
}
public class Additional
{
public string Name { get; set; }
public string Parameter { get; set; }
}`
Should look like this.
And then use. JsonConvert.DeserializeObject<Configuration>(json); To get the object.
If you have Visual studio it has this cool feature called paste special where you can just past your json and it will create a proper class for deserialization. It's under Edit-> Paste special-> Paste json as class

Related

How to get partial properties from JSON returned from HttpClient fast?

I have a JSON snippet here taken from HttpClient class below in a C# .NET 5 program.
Simplified JSON:
{
"Restaurants":
[
{
"Id": 138898,
"Name": "Willesborough Cafe",
"Rating": {
"Count": 76,
"Average": 5.92,
"StarRating": 5.92
},
"CuisineTypes": [
{
"Id": 92,
"IsTopCuisine": false,
"Name": "Breakfast",
"SeoName": "breakfast"
}, {
"Id": 106,
"IsTopCuisine": true,
"Name": "British",
"SeoName": "british"
}
],
"Cuisines": [
{
"Name": "Breakfast",
"SeoName": "breakfast"
}, {
"Name": "British",
"SeoName": "british"
}
]
}
]
}
Current code:
dynamic result =
await _httpClient.GetFromJsonAsync<dynamic>(url);
// dynamic result2 = JsonConvert.DeserializeObject<dynamic>(result); // slow
dynamic result2 = JObject.Parse(result); // slow
I am interested to get the info from each restaurant below from the Restaurants array:
Name
Rating
CusineType
I use dynamic as I do not need to create multiple classes based on the JSON structure & I do not need to change my class if the JSON structure changes.
I have tried JsonConvert.DeserializeObject & JObject.Parse.
However, the Visual Studio debugging stuck at either of the method for a very long period
What is the recommended method to get partial properties from a huge JSON response?
Thanks
You can make a class with named properties
class Restaurant
{
public string Name { get; set; }
public Rating Rating { get; set; }
public List<CuisineType> CuisineTypes { get; set; }
}
class Rating
{
public int Count { get; set; }
public decimal Average { get; set; }
public decimal StarRating { get; set; }
}
class CuisineType
{
public int Id { get; set; }
public bool IsTopCuisine { get; set; }
public string Name { get; set; }
public string SeoName { get; set; }
}
and deserialize json to instance of Restaurant then you have a type you need. That's it.
You need to have a class contains list of Restaurant, because you must have a property equal name with your json object
class RestaurantList { public List<Restaurant> Restaurants {get; set;} }
Now you need a code to bind section of json to object
var restaurants = JsonConvert.DeserializeObject<RestaurantList>(result);

Bind appsettings.json array section to generic class with generic list property

I have to read some values from appsettings.json using IOptionsSnapshot and return them through the API endpoint
"adConfig": [
{
"id": 3,
"adDef": [
{
"type": "number",
"key": "somekey",
"displayName": "Rules",
"values": [
0,
1
],
"regex": null
},
{
"type": "select",
"key": "anotherkey",
"displayName": "Some state",
"values": [
"some string value",
"another string value"
],
"regex": null
}
]
},
{
"id": 1
....
}
]
For this porpose I created 2 classes to use to bind the settings.
public class AdDataModel<T>
{
public string type { get; set; }
public string key{ get; set; }
public string displayName { get; set; }
public List<T> Values { get; set; }
public string regex { get; set; }
}
public class AdConfigurationModel
{
public int Id{ get; set; }
public List<AdDataModel<dynamic>> AdDefinition { get; set; }
}
In the Startup.cs I use this code to bind the settings to the classes:
services.Configure<List<AdConfigurationModel>>(Configuration.GetSection("adConfig"));
So far so good. I inject this into the Controller class but the problem is that AdDataModel.Values are aways of type object<string> ( which is not the case for the first element of adDef:Values subsection). Is there any way to achive Values to be from type int or string based on the values in appsettings.json?
Thanks!

C# Web API Nested JSON from Stored Procedure in MSSQL

Overview:
I'm building an API using ASP.NET Web API 2. I'm building Stored Procedures in SQL and linking these in the API to serve data.
I know that SQL can return JSON using FOR JSON AUTO, for example. But, I don't think it's the best place to configure the data and out put JSON. So I'm assuming there must be a way to achieve nested JSON in the API.
This is what I want to achieve:
{
"Apps": [
{
"ItemID": "1",
"Path": "/AppReport",
"Name": "AppReport",
"Reports": [
{
"Reports_ItemID": "11",
"Reports_Path": "/AppReport/SubReport",
"Reports_Name": "SubReport"
},
{
"Reports_ItemID": "12",
"Reports_Path": "/AppReport/SubReport2",
"Reports_Name": "SubReport2"
}
]
},
{
"ItemID": "2",
"Path": "/AppReport2",
"Name": "AppReport2",
"Reports": [
{
"Reports_ItemID": "22",
"Reports_Path": "/AppReport/SubReport",
"Reports_Name": "SubReport"
}
]
}
]
}
At the moment it's coming out as a flat Array of Objects
{
"Apps": [
{
"ItemID": "1",
"Path": "/AppReport",
"Name": "AppReport",
"Reports_ItemID": "11",
"Reports_Path": "/AppReport/SubReport",
"Reports_Name": "SubReport"
},
{
"ItemID": "1",
"Path": "/AppReport",
"Name": "AppReport",
"Reports_ItemID": "12",
"Reports_Path": "/AppReport/SubReport2",
"Reports_Name": "SubReport2"
},
...
]
}
I have a class of the Stored Procedure in the API that looks like this:
namespace API.Stored_Procedures
{
public partial class SP_GetApps
{
public System.Guid ItemID { get; set; }
public string Path { get; set; }
public string Name { get; set; }
public System.Guid Report_ItemID { get; set; }
public string Report_Path { get; set; }
public string Report_Name { get; set; }
}
}
My Controller looks like this:
[HttpGet]
[Route("{uid}", Name ="getReportApps")]
[ResponseType(typeof(SP_GetApps_Result))]
public IHttpActionResult SP_GetApps(string uid)
{
var res = db.Database.SqlQuery<SP_GetApps_Result>
("SP_GetApps {0}", uid);
return Ok(res);
}
In order to achieve the desired JSON Class should look like
public class App
{
public string ItemID { get; set; }
public string Path { get; set; }
public string Name { get; set; }
public List<Report> Reports { get; set; }
}
public class Report
{
public string Reports_ItemID { get; set; }
public string Reports_Path { get; set; }
public string Reports_Name { get; set; }
}

How do I configure services when I bind an array of objects?

I have a json string attached to the VCAP_SERVICES environment variable that looks like this:
{
"redislabs": [
{
"credentials": {
"host": "redis-1756.pcfredissb2.com",
"ip_list": [
"10.999.46.999"
],
"password": "cz(2u",
"port": 1756
},
"syslog_drain_url": null,
"volume_mounts": [],
"label": "redislabs",
"provider": null,
"plan": "simple-redis",
"name": "sdsredis2",
"tags": [
"redislabs"
]
},
{
"credentials": {
"host": "redis-13610.pcfredis.com",
"ip_list": [
"10.999.46.9999"
],
"password": "n-C*",
"port": 13610
},
"syslog_drain_url": null,
"volume_mounts": [],
"label": "redislabs",
"provider": null,
"plan": "simple-redis",
"name": "sdsredis",
"tags": [
"redislabs"
]
}
]
}
In ConfigureServices in Startup.cs, when I run Configuration.GetSection("redislabs").AsEnumerable() I get something that looks like this:
I have a few options classes that looks like this:
public class RedisLabs
{
public RedisLab[] redislabs { get; set; }
}
public class RedisLab
{
public Credentials credentials { get; set; }
public object syslog_drain_url { get; set; }
public object[] volume_mounts { get; set; }
public string label { get; set; }
public object provider { get; set; }
public string plan { get; set; }
public string name { get; set; }
public string[] tags { get; set; }
}
public class Credentials
{
public string host { get; set; }
public string[] ip_list { get; set; }
public string password { get; set; }
public int port { get; set; }
}
My question is how the heck do I perform the binding on something like this? Is binding an array of objects even possible?
I've tried:
var redislabs = new List<RedisLab>();
Configuration.GetSection("redislabs").Bind(redislabs);
and
services.Configure<RedisLabs>(Configuration);
and a few other methods. Nothing seems to work.
Halp, plz.
Given your json file and classes this should work (binding top level section to instance of a class that that holds the array):
var redisLabs = new RedisLabs();
Configuration.Bind(redisLabs);
This also works:
services.Configure<RedisLabs>(Configuration);
...
var rlOptions = app.ApplicationServices.GetRequiredService<IOptions<RedisLabs>>();
var redisLabs = rlOptions.Value;

Deserializing JSON that has an int as a key in C#

I am trying to deserialize this JSON
{
"39": {
"category": "Miscellaneous",
"country_whitelist": [],
"name": "domain.com",
"url_blacklist": [],
"country_blacklist": [],
"url_whitelist": [
"domain.com"
],
"deals": {
"425215": {
"status": "Ok",
"type": "",
"code": "CODE",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle",
"url": "http://domain.com/foo",
"text": "Text Text Text",
"long_title": "Longer Text"
},
"425216": {
"status": "Ok",
"type": "",
"code": "CODE2",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle2",
"url": "http://domain.com/bar",
"text": "Text Text Text",
"long_title": "Longer Text"
}
},
"88x31": "http://someimage/88x31.png",
"subcategory": "Other"
},
"40": {
"category": "Miscellaneous",
"country_whitelist": [],
"name": "domain.com",
"url_blacklist": [],
"country_blacklist": [],
"url_whitelist": [
"domain.com"
],
"products": {
"425215": {
"status": "Ok",
"type": "",
"code": "CODE",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle",
"url": "http://domain.com/foo",
"text": "Text Text Text",
"long_title": "Longer Text"
},
"425216": {
"status": "Ok",
"type": "",
"code": "CODE2",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle2",
"url": "http://domain.com/bar",
"text": "Text Text Text",
"long_title": "Longer Text"
}
},
"88x31": "http://someimage/88x31.png",
"subcategory": "Other"
}
}
I tried using Json.NET and I tried using ServiceStack's deserializer but I can't seem to get any type of representation for this JSON.
The main thing that is blocking me I believe is that the keys are Int but I don't have control on the JSON I receive.
This is the C# classes I have built
public class product
{
public string status { get; set; }
public string type { get; set; }
public string code { get; set; }
public string end_date { get; set; }
public string title { get; set; }
public string url { get; set; }
public string text { get; set; }
public string long_title { get; set; }
}
public class Merchant
{
public string category { get; set; }
public List<string> country_whitelist { get; set; }
public string name { get; set; }
public List<string> url_blacklist { get; set; }
public List<string> country_blacklist { get; set; }
public List<string> url_whitelist { get; set; }
public List<product> products { get; set; }
public string subcategory { get; set; }
}
public class Data
{
public Dictionary<int, Merchant> MainMerchants { get; set; }
}
I prefer using ServiceStack but any other deserializer that works will be great
var data = client.Get(json);
Getting your data types mapped correctly:
It is possible to deserialize your JSON. As you correctly identified you can deserialize to a Dictionary<int, Merchant>.
But you will need to change your definition of products in the Merchant class to be a Dictionary<int, Product>. It needs to be a dictionary here to handle your numeric key. List<Product> won't work.
Also to handle the 88x31 property you can use a DataMember(Name = '88x31') mapping to map it to something c# likes, like image88x31. Unfortunately this does mean your DTO properties become opt-in so you will then need to decorate all members. Add using System.Runtime.Serialization;
Once you make those changes such that:
// Note I capitalized Product
public class Product
{
public string status { get; set; }
public string type { get; set; }
public string code { get; set; }
public string end_date { get; set; }
public string title { get; set; }
public string url { get; set; }
public string text { get; set; }
public string long_title { get; set; }
}
/*
* Use DataMember to map the keys starting with numbers to an alternative c# compatible name.
* Unfortunately this requires properties to opt in to the data contract.
*/
[DataContract]
public class Merchant
{
[DataMember]
public string category { get; set; }
[DataMember]
public List<string> country_whitelist { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public List<string> url_blacklist { get; set; }
[DataMember]
public List<string> country_blacklist { get; set; }
[DataMember]
public List<string> url_whitelist { get; set; }
[DataMember]
public Dictionary<int, Product> products { get; set; }
[DataMember]
public string sub_category { get; set; }
// This maps the 88x31 key to a c# appropriate name
[DataMember(Name = "88x31")]
public string image88x31 { get; set; }
}
Then you will be able to deserialize into Dictionary<int, Merchant> without any issues.
JsonSerializer.DeserializeFromString<Dictionary<int, Merchant>>("YOUR JSON STRING");
Using in a ServiceStack Service:
If you want to be able to send this request directly to a ServiceStack service, then you can use a RequestBinder to deserialize into this complex type. Given this service:
Request DTO:
[Route("/Merchants", "POST")]
public class MerchantsRequest
{
public Dictionary<int, Merchant> MainMerchants { get; set; }
}
Simple Action Method:
public class MerchantsService : Service
{
public void Post(MerchantsRequest request)
{
var merchant39 = request.MainMerchants.First(p=>p.Key == 39).Value;
Console.WriteLine("Name: {0}\nImage: {1}\nProduct Count: {2}", merchant39.name, merchant39.image88x31, merchant39.products.Count);
var merchant40 = request.MainMerchants.First(p=>p.Key == 40).Value;
Console.WriteLine("Name: {0}\nImage: {1}\nProduct Count: {2}", merchant40.name, merchant40.image88x31, merchant40.products.Count);
}
}
AppHost Configuration:
In your AppHost Configure method you would need to add a binder to the request type. i.e. typeof(MerchantsRequest) like so:
public override void Configure(Funq.Container container)
{
Func<IRequest, object> merchantsRequestBinder = delegate(IRequest request) {
var json = WebUtility.HtmlDecode( request.GetRawBody() );
return new MerchantsRequest { MainMerchants = JsonSerializer.DeserializeFromString<Dictionary<int, Merchant>>(json) };
};
RequestBinders.Add(typeof(MerchantsRequest), merchantsRequestBinder);
...
}
This binder method will convert the json you are sending into a MerchantsRequest. Then you can use it like a regular ServiceStack request.
Full Source Code Here
A fully working example of console application, demonstrating the conversion of the complex JSON to a service request.
Note: I notice in your JSON that you have property deals on one object, and products on another, I assumed this was a typo, as you don't have a corresponding property on in the class for deals.
In your json string, for the products node, should it be this? as the type where it converted from is a List instead of dictionary?
I can get it work change it to following json string
"products": [{
"status": "Ok",
"type": "",
"code": "CODE",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle",
"url": "http://domain.com/foo",
"text": "Text Text Text",
"long_title": "Longer Text"
},
{
"status": "Ok",
"type": "",
"code": "CODE2",
"end_date": "2014-03-01 04:00:00",
"title": "RandomTitle2",
"url": "http://domain.com/bar",
"text": "Text Text Text",
"long_title": "Longer Text"
}],

Categories