Deserialize JSON with dynamic objects - c#

I have a JSON object that comes with a long list of area codes. Unfortunately each area code is the object name on a list in the Data object. How do I create a class that will allow RestSharp to deserialize the content?
Here's how my class looks now:
public class phaxioResponse
{
public string success { get; set; }
public string message { get; set; }
public List<areaCode> data { get; set; }
public class areaCode
{
public string city { get; set; }
public string state { get; set; }
}
}
And here's the JSON content:
{
success: true
message: "277 area codes available."
data: {
201: {
city: "Bayonne, Jersey City, Union City"
state: "New Jersey"
}
202: {
city: "Washington"
state: "District Of Columbia"
} [...]
}

Since this JSON is not C# friendly, I had to do a little bit of hackery to make it come out properly. However, the result is quite nice.
var json = JsonConvert.DeserializeObject<dynamic>(sampleJson);
var data = ((JObject)json.data).Children();
var stuff = data.Select(x => new { AreaCode = x.Path.Split('.')[1], City = x.First()["city"], State = x.Last()["state"] });
This code will generate an anonymous type that best represents the data. However, the anonymous type could be easily replaced by a ctor for a more normal DTO class.
The output looks something like this:

your json is incorrect, but if you do correct it you can use a json-to-csharp tool like the one on http://json2csharp.com/ to generate your classes:
public class __invalid_type__201
{
public string city { get; set; }
public string state { get; set; }
}
public class Data
{
public __invalid_type__201 __invalid_name__201 { get; set; }
}
public class RootObject
{
public bool success { get; set; }
public string message { get; set; }
public Data data { get; set; }
}

I don't know anything about RestSharp, but if you're using Newtonsoft on the server side, then you can just pass a JObject to your method. Then you can interrogate the object to see what type of object it really is and use JObject.ToObject() to convert it.

I think using Dictionary<int,areaCode> is the easiest way.
public class phaxioResponse
{
public string success { get; set; }
public string message { get; set; }
public Dictionary<int,areaCode> data { get; set; }
public class areaCode
{
public string city { get; set; }
public string state { get; set; }
}
}
Then:
var res= JsonConvert.DeserializeObject<phaxioResponse>(json);
Console.WriteLine(string.Join(",", res.data));

Related

Consume Data From this endpoint

I am getting tdata from a certain endpoint and the problem id on serialization to my classes. I want to cast the bellow data to my class but cant get how the class should be structured. Check out the data .....
{
"-LYG_AI_oGYjNBrzMlKF": {
"chatDispayText": "",
"chatId": "-LYG_AI_oGYjNBrzMlKF",
"chatName": "",
"chattype": "single",
"imageUrl": "https://wallpaper.wiki/wp-content/uploads/2017/04/wallpaper.wiki-Amazing-celebrities-hd-wallpaper-PIC-WPD004734.jpg",
"lastMessageSent": "aiye",
"lastMessageSentTime": 1549704416263,
"synched": false,
"users": {
"-LYG_AIZ5MvTbjR7DACe": "Uicpm3L15TX0c15pKCI6KUEARyB3",
"-LYG_AI_oGYjNBrzMlKE": "Xsr0z9lsqNOEytX61lJvaGz1A8F2"
}
}
}
If the data you get out the endpoint has a dynamic structure, you can make use of a key-vale pair collection or a dictionary. For instance:
JObject jObject = JObject.Parse(Data); // This would already give you a key-value pair collection
Dictionary<String,Object> collection = new Dictionary<String, Object>();
foreach(var obj in jObject){
collection.Add(obj.Key, obj.Value);
}
However, this isn't a strongly typed approach which means that it is not effective in the majority of scenarios. A better solution when dealing with endpoints would be to define a class with fixed schema, actually something you need in your code, and then map the class to the object yielded by the endpoint using a metadata struct. For example:
public class ChatInfoModel
{
[JsonProperty(Metadata.ChatId)]
public long ChatId { get; set; }
[JsonProperty(Metadata.ChatId, Required = Required.AllowNull)]
public String Message { get; set; }
}
public struct Metadata
{
public const String ChatId = "userChatId";
public const String Message = "messageTxt";
}
And then
var deserializedObject = JsonConvert.DeserializeObject<ChatInfoModel>(data);
However, if your class has the exact same naming convention (but should not necessarily follow the camelCase naming convention) for its properties as in the serialized data, the JsonProperty attribute would not be needed.
You can also deserialize the object without using JsonProperty attribute manually using the first approach, and it is actually advantageous in certain scenarios where your schema comes from a configuration file rather than a struct.
Take inspiration from the Structure below:
public class Rootobject
{
public LYG_AI_Ogyjnbrzmlkf LYG_AI_oGYjNBrzMlKF { get; set; }
}
public class LYG_AI_Ogyjnbrzmlkf
{
public string chatDispayText { get; set; }
public string chatId { get; set; }
public string chatName { get; set; }
public string chattype { get; set; }
public string imageUrl { get; set; }
public string lastMessageSent { get; set; }
public long lastMessageSentTime { get; set; }
public bool synched { get; set; }
public Users users { get; set; }
}
public class Users
{
public string LYG_AIZ5MvTbjR7DACe { get; set; }
public string LYG_AI_oGYjNBrzMlKE { get; set; }
}

Put JSON data into c# datatable

I am trying to deserialise some JSON that I get back from an API so that I can loop through an array of county names and add the information to a datatable in C#. However I am receiving following error at the first hurdle when I try and deserialise it:
error: System.MissingMethodException: No parameterless constructor defined for type of 'DPDJSONLibrary.DPD_JSON+LOCR_Data[]'.
The provider of the API provides an example of the JSON response as follows:
{
"error": null,
"data":[{
"country": [{
"countryCode":"GB",
"countryName":"United Kingdom",
"internalCode":"UK",
"isEUCountry":false,
"isLiabilityAllowed":false,
"isoCode":"826",
"isPostcodeRequired":false,
"liabilityMax":15000
}]
}]
}
A sample of the JSON data I am getting back from the API is:
{
"data": {
"country":[
{
"countryCode":"PM",
"countryName":"St Pierre & Miquilon",
"isoCode":"666",
"isEUCountry":false,
"isLiabilityAllowed":true,
"liabilityMax":15000,
"isPostcodeRequired":true
},
{
"countryCode":"SR",
"countryName":"Suriname",
"isoCode":"740",
"isEUCountry":false,
"isLiabilityAllowed":true,
"liabilityMax":15000,
"isPostcodeRequired":true
},
{
"countryCode":"SZ",
"countryName":"Swaziland",
"isoCode":"748",
"isEUCountry":false,
"isLiabilityAllowed":true,
"liabilityMax":15000,
"isPostcodeRequired":true
}
]
}
}
I have tried to make some classes to put the JSON in as follows:
/// <summary>
/// List Of Countries Response object.
/// </summary>
public class LOCR
{
public LOCR_Error error { get; set; }
public LOCR_Data[] data { get; set; }
}
public class LOCR_Error
{
public string errorAction { get; set; }
public string errorCode { get; set; }
public string errorMessage { get; set; }
public string errorObj { get; set; }
public string errorType { get; set; }
}
public class LOCR_Data
{
public LOCR_Data_Country[] country { get; set; }
}
public class LOCR_Data_Country
{
public string countryCode { get; set; }
public string countryName { get; set; }
public string internalCode { get; set; }
public bool isEUCountry { get; set; }
public bool isLiabilityAllowed { get; set; }
public string isoCode { get; set; }
public bool isPostcodeRequired { get; set; }
public int liabilityMax { get; set; }
}
When I get the JSON back as a string, I am trying to use the Newtonsoft (plugin?) to put it into my classes using:
JavaScriptSerializer ser = new JavaScriptSerializer();
DPD_JSON.LOCR DPDCountries = new DPD_JSON.LOCR();
DPDCountries = ser.Deserialize<DPD_JSON.LOCR>(data);
It is the last line above that is generating the error. I suspect I've written my classes wrong that I am trying to deserialise the JSON in to - can anyone see where I've gone wrong?
Deserialize will return a list and not an array, So your LOCR_Data_Country should be of type List and not array:
public class LOCR_Data
{
public List<LOCR_Data_Country> country { get; set; }
}
There's a HUGE difference between the two example JSON strings you've shown. Mainly the first one is an array : "data":[ ... ] and the second one is an object "data:{ ... }. These two are not interchangeable so you have to stick to either one of those. If the thing you're getting back from the API is an object instead you should rewrite your model to be :
public class LOCR
{
public LOCR_Error error { get; set; }
// object here since "data": { ... }
public LOCR_Data data { get; set; }
}
And as you move further with the JSON you can see that LOCR_Data.country is in fact an array in both cases "country": [ ... ] so you can stick with the current implementation of LOCR_Data class.
Try Using :
YourResultClass object = JsonConvert.DeserializeObject<YourResultClass>(Jsonstring);
See the answer of this Using JsonConvert.DeserializeObject to deserialize Json
OR
dynamic data = Json.Decode(json);
You can refer this Deserialize JSON into C# dynamic object? for further assistance

Read JSON file into a Jarray and access data dynamically

I am trying to read in a json file which is very complex into a JArray that can be accessed dynamically in a foreach statement. However I am getting an error which is stating 'Current JsonReader item is not an Array' I am able to access the keys and values if I use a JObject but that is not what I need I need to be able to go through the records and search by names or IDs, etc. I am a bit new at newtwonsoft and JSON.net so any help would be appreciated!
JArray o1 = JArray.Parse(File.ReadAllText(#"myfilepath"));
dynamic records = o1;
foreach (dynamic record in records)
{
Console.WriteLine(record.Id + " (" + record.Name + ")");
}
My json file looks like this, there are multiple records however I have shortened it. I need to access and search by Id or Name however I am having issues accessing.
String json = #"{
"RecordSetBundles" : [ {
"Records" : [ {
"attributes" : {
"type" : "nihrm__Location__c",
"url" : "/services/data/v38.0/sobjects/nihrm__Location__c/a0di0000001nI3oAAE"
},
"CurrencyIsoCode" : "USD",
"Id" : "a0di0000001nI3oAAE",
"Name" : "The City Hotel",
"nihrm__Abbreviation__c" : "TCH",
"nihrm__AddressLine1__c" : "1 Main Street",
"nihrm__AlternateNameES__c" : "El Ciudad Hotel",
"nihrm__AvailabilityScreenView__c" : "Combined",
"nihrm__Geolocation__c" : null,
"nihrm__HideBookingsFromPortal__c" : false,
"nihrm__IsActive__c" : true,
"nihrm__IsPlannerPortalEnabled__c" : false,
"nihrm__isRemoveCancelledEventsFromBEOs__c" : false,
"nihrm__ManagementAffliation__c" : "NI International",
"nihrm__NearestAirportCode__c" : "MHT",
"nihrm__Phone__c" : "(207) 555-5555",
"nihrm__PostalCode__c" : "04103",
"nihrm__PropertyCode__c" : "TCH",
"nihrm__RegionName__c" : "Northeast",
"nihrm__RestrictBookingMoveWithPickup__c" : true,
"nihrm__RohAllowedStatuses__c" : "Prospect;Tentative;Definite",
"nihrm__StateProvince__c" : "ME",
"nihrm__SystemOfMeasurement__c" : "Standard",
"nihrm__TimeZone__c" : "GMT-05:00 Eastern Daylight Time (America/New_York)",
"nihrm__UpdateBookingEventAverageChecks__c" : false,
"nihrm__UseAlternateLanguage__c" : false,
"nihrm__Website__c" : "www.thecityhotelweb.com"
} ],
"ObjectType" : "nihrm__Location__c",
"DeleteFirst" : false
},
Here is a link to the entire json:
https://codeshare.io/rGL6K5
The other answers show strong typed classes. If you can do this and you're sure the structure won't change I'd recommend doing it that way. It'll make everything else much easier.
If you want to do it with a dynamic object, then you can get what you want this way.
// GET JSON DATA INTO DYNAMIC OBJECT
var data = JsonConvert.DeserializeObject<dynamic>(File.ReadAllText(#"myfilepath"));
// GET TOP LEVEL "RecordSetBundles" OBJECT
var bundles = data.RecordSetBundles;
// LOOP THROUGH EACH BUNDLE OF RECORDS
foreach (var bundle in bundles)
{
// GET THE RECORDS IN THIS BUNDLE
var records = bundle.Records;
// LOOP THROUGH THE RECORDS
foreach (var record in records)
{
// WRITE TO CONSOLE
Console.WriteLine(record.Id.ToString() + " (" + record.Name.ToString() + ")");
}
}
Produces this output:
a0di0000001nI3oAAE (The City Hotel)
a0xi0000000jOQCAA2 (Rounds of 8)
a0xi0000001aUbfAAE (Additional Services)
a0xi0000004ZnehAAC (Citywide)
a0xi0000001YXcCAAW (Cocktail Rounds)
etc etc
Do you know if the properties of your JSon object will be the same each time the request is made? Not the values, just the names? If so, create a concrete type and use JSonObjectSerializer to deserialize into an object. If should be able to create a dictionary collection on that object that deserialized your JSon data into Key Value pairs. Then you can iterate like any other collection via the Keys collection.
You don't really need to use dynamic objects unless you really don't know what you are dealing with on the input. Ascertain if your incoming data has consistent property names each time the request is made.
An example could be
var myData = JsonSerializer.Deserialize<myDataType>(incomingTextData);
myDataType would have a collection that can handle your array.
This error Current JsonReader item is not an Array is self explanatory: the object parsed is not an array.However without see the Json we can only suppose that this Json is an object and if you want use a dynamic object change the code as follow:
dynamic myJson = JsonConvert.DeserializeObject(File.ReadAllText(#"myfilepath"));
var currency = myJson.RecordSetBundles[0].Records[0].CurrencyIsoCode
//Access to other property in your dynamic object
EDIT
Your JSON object seems quite complex and access via dynamic can be complex I suggest you to use JsonConvert.DeserializeObject<T>(string).
From the json that yoou have posted the class looks like this:
public class Attributes
{
public string type { get; set; }
public string url { get; set; }
}
public class Record
{
public Attributes attributes { get; set; }
public string CurrencyIsoCode { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public string nihrm__Abbreviation__c { get; set; }
public string nihrm__AddressLine1__c { get; set; }
public string nihrm__AlternateNameES__c { get; set; }
public string nihrm__AvailabilityScreenView__c { get; set; }
public object nihrm__Geolocation__c { get; set; }
public bool nihrm__HideBookingsFromPortal__c { get; set; }
public bool nihrm__IsActive__c { get; set; }
public bool nihrm__IsPlannerPortalEnabled__c { get; set; }
public bool nihrm__isRemoveCancelledEventsFromBEOs__c { get; set; }
public string nihrm__ManagementAffliation__c { get; set; }
public string nihrm__NearestAirportCode__c { get; set; }
public string nihrm__Phone__c { get; set; }
public string nihrm__PostalCode__c { get; set; }
public string nihrm__PropertyCode__c { get; set; }
public string nihrm__RegionName__c { get; set; }
public bool nihrm__RestrictBookingMoveWithPickup__c { get; set; }
public string nihrm__RohAllowedStatuses__c { get; set; }
public string nihrm__StateProvince__c { get; set; }
public string nihrm__SystemOfMeasurement__c { get; set; }
public string nihrm__TimeZone__c { get; set; }
public bool nihrm__UpdateBookingEventAverageChecks__c { get; set; }
public bool nihrm__UseAlternateLanguage__c { get; set; }
public string nihrm__Website__c { get; set; }
}
public class RecordSetBundle
{
public List<Record> Records { get; set; }
public string ObjectType { get; set; }
public bool DeleteFirst { get; set; }
}
public class RootObject
{
public List<RecordSetBundle> RecordSetBundles { get; set; }
}
To deserialize it using Newtonsoft:
var myObject = JsonConvert.DeserializeObject<RootObject>(myJsonStrin);

Deserialise JSON containing numeric key with Json.NET

I would like to deserialize the following JSON (using Json.NET) to an object, but cannot, as the class name would need to begin with a number.
An example of this is the Wikipedia article API. Using the API to provide a JSON response returns something like this. Note the "16689396" inside the "pages" key.
{
"batchcomplete":"",
"continue":{
"grncontinue":"0.893378504602|0.893378998188|35714269|0",
"continue":"grncontinue||"
},
"query":{
"pages":{
"16689396":{
"pageid":16689396,
"ns":0,
"title":"Jalan Juru",
"extract":"<p><b>Jalan Juru</b> (Penang state road <i>P176</i>) is a major road in Penang, Malaysia.</p>\n\n<h2><span id=\"List_of_junctions\">List of junctions</span></h2>\n<p></p>\n<p><br></p>"
}
}
}
}
How could I deserialize this JSON containing a number which changes based on the article?
It sounds like the Pages property in your Query class would just need to be a Dictionary<int, Page> or Dictionary<string, Page>.
Complete example with the JSON you've provided - I've had to guess at some of the name meanings:
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
public class Root
{
[JsonProperty("batchcomplete")]
public string BatchComplete { get; set; }
[JsonProperty("continue")]
public Continuation Continuation { get; set; }
[JsonProperty("query")]
public Query Query { get; set; }
}
public class Continuation
{
[JsonProperty("grncontinue")]
public string GrnContinue { get; set; }
[JsonProperty("continue")]
public string Continue { get; set; }
}
public class Query
{
[JsonProperty("pages")]
public Dictionary<int, Page> Pages { get; set; }
}
public class Page
{
[JsonProperty("pageid")]
public int Id { get; set; }
[JsonProperty("ns")]
public int Ns { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("extract")]
public string Extract { get; set; }
}
class Test
{
static void Main()
{
string text = File.ReadAllText("test.json");
var root = JsonConvert.DeserializeObject<Root>(text);
Console.WriteLine(root.Query.Pages[16689396].Title);
}
}
Related question: Json deserialize from wikipedia api with c#
Essentially you need to changes from using a class for the pages to a dictionary, which allows for the dynamic nature of the naming convention.
Class definitions :
public class pageval
{
public int pageid { get; set; }
public int ns { get; set; }
public string title { get; set; }
public string extract { get; set; }
}
public class Query
{
public Dictionary<string, pageval> pages { get; set; }
}
public class Limits
{
public int extracts { get; set; }
}
public class RootObject
{
public string batchcomplete { get; set; }
public Query query { get; set; }
public Limits limits { get; set; }
}
Deserialization :
var root = JsonConvert.DeserializeObject<RootObject>(__YOUR_JSON_HERE__);
var page = responseJson.query.pages["16689396"];
You can implement your own DeSerializer or editing the JSON before you DeSerialize it.

Deserialize nested JSON Response with RestSharp Client

I'd like to consume a REST Api and deserialize the nested JSON Response. For that purpose I tried to create some POCO classes which represent the JSON Response [1].
The response looks like this:
{
"success": true,
"message": "OK",
"types":
[
{
"name": "A5EF3-ASR",
"title": "ITIL Foundation Plus Cloud Introduction",
"classroomDeliveryMethod": "Self-paced Virtual Class",
"descriptions": {
"EN": {
"description": "some Text null",
"overview": null,
"abstract": "Some other text",
"prerequisits": null,
"objective": null,
"topic": null
}
},
"lastModified": "2014-10-08T08:37:43Z",
"created": "2014-04-28T11:23:12Z"
},
{
"name": "A4DT3-ASR",
"title": "ITIL Foundation eLearning Course + Exam",
"classroomDeliveryMethod": "Self-paced Virtual Class",
"descriptions": {
"EN": {
"description": "some Text"
(...)
So I created the following POCO classes:
public class Course
{
public bool success { get; set; }
public string Message { get; set; }
public List<CourseTypeContainer> Type { get; set; }
}
/* each Course has n CourseTypes */
public class CourseType
{
public string Name { get; set; }
public string Title { get; set; }
public List<CourseTypeDescriptionContainer> Descriptions { get; set; }
public DateTime LastModified { get; set; }
public DateTime Created { get; set; }
}
public class CourseTypeContainer
{
public CourseType CourseType { get; set; }
}
/* each CourseType has n CourseTypeDescriptions */
public class CourseTypeDescription
{
public string Description { get; set; }
public string Overview { get; set; }
public string Abstract { get; set; }
public string Prerequisits { get; set; }
public string Objective { get; set; }
public string Topic { get; set; }
}
public class CourseTypeDescriptionContainer
{
public CourseTypeDescription CourseTypeDescription { get; set; }
}
And this is the API Code:
var client = new RestClient("https://www.someurl.com");
client.Authenticator = new HttpBasicAuthenticator("user", "password");
var request = new RestRequest();
request.Resource = "api/v1.0/types";
request.Method = Method.GET;
request.RequestFormat = DataFormat.Json;
var response = client.Execute<Course>(request);
EDIT 1: I found a Typo, the Type property in AvnetCourse should be named Types:
public List<AvnetCourseTypeContainer> Type { get; set; } // wrong
public List<AvnetCourseTypeContainer> Types { get; set; } // correct
Now the return values look like:
response.Data.success = true // CORRECT
repsonse.Data.Message = "OK" // CORRECT
response.Data.Types = (Count: 1234); // CORRECT
response.Data.Types[0].AvnetCourseType = null; // NOT CORRECT
EDIT 2: I implemented the Course.Types Property using a List<CourseType> instead of a List<CourseTypeContainer>, as proposed by Jaanus. The same goes for the CourseTypeDescriptionContainer:
public List<CourseTypeContainer> Type { get; set; } // OLD
public List<CourseTypeDescriptionContainer> Descriptions { get; set; } // OLD
public List<CourseType> Type { get; set; } // NEW
public List<CourseTypeDescription> Descriptions { get; set; } // NEW
Now the response.Data.Types finally are properly filled. However, the response.Data.Types.Descriptions are still not properly filled, since there is an additional language layer (e.g. "EN"). How can I solve this, without creating a PACO for each language?
EDIT 3: I had to add an additional CourseTypeDescriptionDetails class, where I would store the descriptive Data. In my CourseTypeDescription I added a property of the Type List for each language. Code Snippet:
public class AvnetCourseType
{
public List<CourseTypeDescription> Descriptions { get; set; }
// other properties
}
public class CourseTypeDescription
{
public List<CourseTypeDescriptionDetails> EN { get; set; } // English
public List<CourseTypeDescriptionDetails> NL { get; set; } // Dutch
}
public class CourseTypeDescriptionDetails
{
public string Description { get; set; }
public string Overview { get; set; }
public string Abstract { get; set; }
public string Prerequisits { get; set; }
public string Objective { get; set; }
public string Topic { get; set; }
}
It works now, but I need to add another property to CourseTypeDescription for each language.
OLD: The return values are
response.Data.success = true // CORRECT
repsonse.Data.Message = "OK" // CORRECT
response.Data.Type = null; // WHY?
So why does my response.Type equal null? What am I doing wrong?
Thank you
Resources:
[1] RestSharp Deserialization with JSON Array
Try using this as POCO:
public class Course
{
public bool success { get; set; }
public string message { get; set; }
public List<CourseTypeContainer> Types { get; set; }
}
Now you have list of CourseTypeContainer.
And CourseTypeContainer is
public class CourseTypeContainer
{
public CourseType CourseType { get; set; }
}
So when you are trying to get response.Data.Types[0].AvnetCourseType , then you need to have field AvnetCourseType inside CourseTypeContainer
Or I think what you want is actually this public List<CourseType> Types { get; set; }, you don't need a container there.
Just in case this helps someone else, I tried everything here and it still didn't work on the current version of RestSharp (106.6.2). RestSharp was completely ignoring the RootElement property as far as I could tell, even though it was at the top level. My workaround was to manually tell it to pull the nested JSON and then convert that. I used JSON.Net to accomplish this.
var response = restClient.Execute<T>(restRequest);
response.Content = JObject.Parse(response.Content)[restRequest.RootElement].ToString();
return new JsonDeserializer().Deserialize<T>(response);
I used http://json2csharp.com/ to create C# classes from JSON.
Then, renamed RootObject to the ClassName of the model file I'm creating
All the data in the nested json was accessible after RestSharp Deserializitaion similar to responseBody.data.Subject.Alias
where data, Subject and Alias are nested nodes inside the response JSON received.

Categories