How to ignore empty or not setted json key? - c#

I have a json like this:
{
"user": [
{
"det": [
{
"Code": "9",
"Description": "Update"
}
],
"arts": []
}
]
}
Now in some case the json that I reiceve contains the key arts in other case this key isn't provided, so simply I have only the json with the det key as:
{
"user": [
{
"det": [
{
"Code": "9",
"Description": "Update"
}
]
}
]
}
and this is my class for deserializing it:
public class Det
{
public string Code { get; set; }
public string Description { get; set; }
}
public class Arts
{
public string ID { get; set; }
public string CON3 { get; set; }
}
public class user
{
public List<Det> det { get; set; }
public List<Arts> arts {get; set; }
}
public class RootObject
{
public List<Det> user { get; set; }
}
So when I receive the first json the code working well, but when I receive the second the code fall in exception:
Cannot deserialize the current JSON object
This because the arts key isn't provided but there is only the det key. So there is a way to tell to my code to ignore arts if is not setted? How can I do this? Thanks.

Related

How to deserialise this nested json response and save the the array values with multiple Jobjects (in Unity)

I have little to no experience in JSON and I am stuck with a problem. Any help is appreciated.
I want to access specifically the names' values from the additionalInformation array.
JSON Response:
{
"statusCode": 200,
"version": 1,
"jsonData": [
{
"additionalInformation": [
{
"id": "XXX94XXXX9xxXx_xxxXXXX",
"name": "xxxx xxx x xxxxxxxx"
},
{
"id": "0xXXxcXxv5PQqT$6i2zLgV",
"name": "xxx xxxxxxxx"
},
{
"id": "11Krt_our2rPCPqJ_2fKZR",
"name": "xxx xxxxxxxx xx"
},
{
"id": "2jYw4IyBP8KuozM_ej7DGf",
"name": "xxxxxxx 1"
},
{
"id": "3B8O805wL1ufabHMz1Je3v",
"name": "xxxxxxx 2"
},
{
"id": "0FVKUYZkvFaxd_OQUiyPBZ",
"name": "xxxxxxx"
},
{
"id": "3O41QFd0573QQvFco5zUUP",
"name": "Xxxxxxxxx"
}
],
"type": 0
}
],
"errorMessages": [],
"warningMessages": [],
"informationMessages": []
}
Model:
public class CFunctions
{
public int statusCode { get; set; }
public int version { get; set; }
public List<PFunctions>[] jsonData { get; set; }
public List<string> errorMessages { get; set; }
public List<string> warningMessages { get; set; }
public List<string> informationMessages { get; set; }
/*public CFunctions()
{
jsonData = new List<PFunctions>();
}*/
}
[Serializable]
public class PFunctions
{
public List<PAdditionalInfo>[] additionalInformation { get; set; }
public int type { get; set; }
/*public PFunctions()
{
additionalInformation = new List<PAdditionalInfo>();
}*/
}
[Serializable]
public class PAdditionalInfo
{
public Guid id { get; set; }
public string name { get; set; }
}
Deserialisation
var request = UnityWebRequest.Get(baseurl);
var operation = request.SendWebRequest();
var jsonResponse = request.downloadHandler.text;
List<CFunctions>[] PFunctionsList = JsonConvert.DeserializeObject<List<CFunctions>[]>(jsonResponse);
Error:
Cannot deserialize the current JSON object into type 'System.Collections.Generic.List`1[CFunctions][]' because the type requires a JSON array to deserialize correctly.
To fix this error either change the JSON to a JSON array or change the deserialized type so that it is a normal .NET type 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 'statusCode', line 1, position 14.
UnityEngine.Debug:Log(Object)
What I tried
The error pertains even when I changed List<PAdditionalInfo> to List<PAdditionalInfo>[]
I am not sure how to use JsonObjectAttribute and if it is the best way.
You've declared an array of List<T> in the models, eg List<PAdditionalInfo>[]. The json represents single arrays, not nested. You can fix that by choosing one or the other (I decided to use List<> but array is valid too):
public class PFunctions
{
public List<PAdditionalInfo> additionalInformation { get; set; } // removed []
...
}
public class CFunctions
{
public int statusCode { get; set; }
public int version { get; set; }
public List<PFunctions> jsonData { get; set; } // removed []
...
}
The class you're deserializing to is incorrect. Deserialize to the correct type (which is CFunctions not List<CFunctions>[]):
CFunctions cFunctions = JsonConvert.DeserializeObject<CFunctions>(json);
the most efficient way to get an additional information is this one line code and you only need one class
List<AdditionalInformation> additionalInformation = JObject.Parse(json)
["jsonData"][0]["additionalInformation"].ToObject<List<AdditionalInformation>>();
class
public class AdditionalInformation
{
public string id { get; set; }
public string name { get; set; }
}

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);

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"
}],

AJAX POST Complex JSON to MVC4 Controller

I have a complex JSON object that I'd like to pass to a MVC4 Controller route.
{
"name": "Test",
"description": "Description",
"questions": [
{
"id": "1",
"type": "1",
"text": "123",
"answers": [
{
"answer": "123",
"prerequisite": 0
},
{
"answer": "123",
"prerequisite": 0
}
],
"children": [
{
"id": "2",
"type": "2",
"text": "234",
"answers": [
{
"answer": "234",
"prerequisite": 0
},
{
"answer": "234",
"prerequisite": 0
}
],
"children": []
}
]
}
]
I have these ViewModels defined:
public class FormDataTransformContainer
{
public string name { get; set; }
public string description { get; set; }
public QuestionDataTransformContainer[] questions;
}
public class QuestionDataTransformContainer {
public int type { get; set; }
public string text { get; set; }
public AnswerDataTransformContainer[] answers { get; set; }
public QuestionDataTransformContainer[] children { get; set; }
}
public class AnswerDataTransformContainer {
public string answer { get; set; }
public int prerequisite { get; set; }
}
And this is the route I'm hitting:
[HttpPost]
public ActionResult Create(FormDataTransformContainer formData)
{
Currently, the name and description property on FormDataTransformContainer are set, but the questions array is null. I hoped that the Data Binding would figure it out, but I assume the tree nature of the data structure is a little complex for it. If I'm correct what is the best solution to this?
questions should be a property, not a field. I'd also change from arrays to IList<> (assuming your serialization library handles that well), because that's probably closer to what it should be, and lets you use a more generic interface instead of a specific implementation.
public class FormDataTransformContainer
{
public string name { get; set; }
public string description { get; set; }
public IList<QuestionDataTransformContainer> questions { get; set; }
}
public class QuestionDataTransformContainer {
public int type { get; set; }
public string text { get; set; }
public IList<AnswerDataTransformContainer> answers { get; set; }
public IList<QuestionDataTransformContainer> children { get; set; }
}
public class AnswerDataTransformContainer {
public string answer { get; set; }
public int prerequisite { get; set; }
}
I've tested this structure with Json.net (MVC4's default, I believe), and it works.
As #robert-harvey said, you should utilize libraries like JSON.NET that are already available to do the heavy lifting for you.
Pulled from the JSON.NET API docs:
If you create a string json that holds your json, you can read from it with new JsonTextReader(new StringReader(json))
I a similar problem, solved with the following code:
public class ExtendedController : Controller
{
public T TryCreateModelFromJson<T>(string requestFormKey)
{
if (!this.Request.Form.AllKeys.Contains(requestFormKey))
{
throw new ArgumentException("Request form doesn't contain provided key.");
}
return
JsonConvert.DeserializeObject<T>(
this.Request.Form[requestFormKey]);
}
}
And usage:
[HttpPost]
[ActionName("EditAjax")]
public ActionResult EditAjaxPOST()
{
try
{
var viewModel =
this.TryCreateModelFromJson<MyModel>(
"viewModel");
this.EditFromModel(viewModel);
return
this.JsonResponse(
this.T("Model updated successfuly."),
true);
}
catch (Exception ex)
{
this.Logger.Error(ex, "Error while updating model.");
return this.JsonResponse(this.T("Error"), false);
}
}
Called from JS:
function saveViewModel() {
$.post(
'#Url.Action("EditAjax")',
{
__RequestVerificationToken: '#Html.AntiForgeryTokenValueOrchard()',
viewModel: ko.mapping.toJSON(viewModel)
},
function (data) {
// response
});
}
Used additional library for deserializing/serializing JSON: http://www.nuget.org/packages/Newtonsoft.Json

Categories