Read JSON file into a Jarray and access data dynamically - c#

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

Related

How to deserialize root JSON dynamic property name in C#? [duplicate]

I've got the following json document:
{
"name": "bert",
"Bikes": {
"Bike1": {
"value": 1000,
"type": "Trek"
},
"Bike2": {
"value": 2000,
"type": "Canyon"
}
}
}
With potentially other bikes like Bike3...BikeN. I want to deserialize to C# objects. Problem is that in the deserialization step the bikes data is completely lost, resulting in a null Bikes collection.
Code to reproduce:
[Test]
public void FirstCityJsonParsingTest()
{
var file = #"./testdata/test.json";
var json = File.ReadAllText(file);
var res = JsonConvert.DeserializeObject<Person>(json);
Assert.IsTrue(res.Name == "bert");
// next line is failing, because res.Bikes is null...
Assert.IsTrue(res.Bikes.Count == 2);
}
public class Bike
{
public string Id { get; set; }
public int Value { get; set; }
public string Type { get; set; }
}
public class Person
{
public string Name { get; set; }
public List<Bike> Bikes { get; set; }
}
To fix this problem a change in the used model is necessary. But what change is needed here to fill the bikes data correctly?
Note: Changing the input document is not an option (as it's a spec)
Your code structure is not reflecting your json. Common approach to deserializing json with dynamic property names is to use Dictionary<string, ...> (supported both by Json.NET and System.Text.Json). Try the following:
public class Bike
{
public int Value { get; set; }
public string Type { get; set; }
}
public class Person
{
public string Id { get; set; }
public string Name { get; set; }
public Dictionary<string, Bike> Bikes { get; set; }
}
Person.Bikes should be changed to Dictionary<string, Bike> (also Bike.Id property is not needed) cause Bikes json element is not an array but object.

Deserialize json object with dynamic items in C#

I've got the following json document:
{
"name": "bert",
"Bikes": {
"Bike1": {
"value": 1000,
"type": "Trek"
},
"Bike2": {
"value": 2000,
"type": "Canyon"
}
}
}
With potentially other bikes like Bike3...BikeN. I want to deserialize to C# objects. Problem is that in the deserialization step the bikes data is completely lost, resulting in a null Bikes collection.
Code to reproduce:
[Test]
public void FirstCityJsonParsingTest()
{
var file = #"./testdata/test.json";
var json = File.ReadAllText(file);
var res = JsonConvert.DeserializeObject<Person>(json);
Assert.IsTrue(res.Name == "bert");
// next line is failing, because res.Bikes is null...
Assert.IsTrue(res.Bikes.Count == 2);
}
public class Bike
{
public string Id { get; set; }
public int Value { get; set; }
public string Type { get; set; }
}
public class Person
{
public string Name { get; set; }
public List<Bike> Bikes { get; set; }
}
To fix this problem a change in the used model is necessary. But what change is needed here to fill the bikes data correctly?
Note: Changing the input document is not an option (as it's a spec)
Your code structure is not reflecting your json. Common approach to deserializing json with dynamic property names is to use Dictionary<string, ...> (supported both by Json.NET and System.Text.Json). Try the following:
public class Bike
{
public int Value { get; set; }
public string Type { get; set; }
}
public class Person
{
public string Id { get; set; }
public string Name { get; set; }
public Dictionary<string, Bike> Bikes { get; set; }
}
Person.Bikes should be changed to Dictionary<string, Bike> (also Bike.Id property is not needed) cause Bikes json element is not an array but object.

Deserialize Nested JSON into classes/objects, in turn - serialize it back into string

Here's my goal, I want to retrieve JSON data from a source, deserialize into objects, filter out objects based on value, and finally serialize it back into JSON string format. Here's (an example) what I have for JSON data:
[
{
"property":"prop1",
"status" : {
status1: "A",
status2: -1,
status3: "Success",
},
"offlist":[
{
"description":"description blah",
"type":"F",
"values":{
"value1":30.0,
"value2":0.0
}
},
{
"description":"description blah",
"type":"F",
"values":{
"value1":30.0,
"value2":0.0
}
}
]
},
{
"property":"prop2",
"status" : {
status1: "A",
status2: -1,
status3: "Success",
},
"offlist":[
{
"description":"description blah",
"type":"Q",
"values":{
"value1":30.0,
"value2":0.0
}
}
]
}
]
Here's my classes:
public class offerModel {
public List<offlist> offlist { get; set; }
public status statuses { get; set; }
public string property{ get; set; }
}
public class offlist{
public string description{ get; set; }
public string type{ get; set; }
public values values { get; set; }
}
public class values{
public double value1 { get; set; }
public double value2{ get; set; }
}
public class statuses {
public string status1 { get; set; }
public double status2 { get; set; }
public string status3 { get; set; }
}
public class RootObj {
public List<offerModel> offModels { get; set; }
}
Upon attempt of deserializing into objects, I get a List of 5 objects back (Which is intended), but all those objects are null. See Below:
var obj = JsonConvert.DeserializeObject<List<RootObj>>(jsonstring);
I then want to remove any 'offlist' objects with the 'type' value equaling 'F'.
After removing those, I then want to put back into JSON string format, which I believe would be very similar to below, I'm just not able to get it to deserialize in the correct fashion.
var json = JsonConvert.SerializeObject(obj);
Let me know if I left any details out...
Deserialize nested JSON into Class. not on dictionary based but it's useful.
Step 01: open the link https://jsonformatter.org/json-parser
Step 02: copy your json file or contents.
Step 03: Open above link. copy contents and past in to left side and click on to JSON Parser button.
Step 04: Click on download button. Downloading the jsonformatter.txt file. Successfully download the file as jsonformatter.txt.
Step 05: Copy step 02 content and open url https://json2csharp.com/.Copy contents and past in to left side and click on to Convert button.
Step 06: In Scripting.
(A) Create myRootClass.cs file and copy and past down contents to your file.[[System.Serializable] it's used in unity 3d software c# scripting]
[System.Serializable]
public class myRootClass
{
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
[System.Serializable]
public class Status
{
public string status1 { get; set; }
public int status2 { get; set; }
public string status3 { get; set; }
}
[System.Serializable]
public class Values
{
public double value1 { get; set; }
public double value2 { get; set; }
}
[System.Serializable]
public class Offlist
{
public string description { get; set; }
public string type { get; set; }
public Values values { get; set; }
}
public class Root
{
public string property { get; set; }
public Status status { get; set; }
public List<Offlist> offlist { get; set; }
}
}
(B) Read the jsonformatter.txt file.
// Read entire text file content in one string
string textFilePath = "C:/Users/XXX/Downloads/jsonformatter.txt";//change path
string jsontext = System.IO.File.ReadAllText(textFilePath);
Debug.Log("Read Json"+jsontext);// used Console.Writeline
(C) Convert string into C# and show the data.
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(jsontext);
Debug.Log("myDeserializedClass.property:- "+myDeserializedClass.property); //used Console.Writeline
var offlists= myDeserializedClass.offlist; // used foreach and view data
Change serialization to JsonConvert.DeserializeObject<List<offerModel>>(data)
and fix offerModel class property statuses to public statuses status { get; set; } to make your sample deserializable
If you want to keep a root object you can do next:
public class RootObj : List<offerModel>{}
JsonConvert.DeserializeObject<RootObj>(data)

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.

Deserialize JSON with dynamic objects

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

Categories