Parsing a complex JSON result with C# - c#

I am trying to parse the following complex JSON result, which is returned from the Zoho Crm API:
{
"response":
{
"result":
{
"Contacts":
{
"row":
[
{
"no":"1",
"FL":
[
{
"content":"555555000000123456",
"val":"CONTACTID"
},
{
"content":"555555000000012345",
"val":"SMOWNERID"
},
{
"content":"John Doe",
"val":"Contact Owner"
},
{
"content":"Pete",
"val":"First Name"
},
{
"content":"Smith",
"val":"Last Name"
},
{
"content":"pete#mail.com",
"val":"Email"
},
{
"content":"5555551000000012346",
"val":"SMCREATORID"
},
{
"content":"Jane Doe",
"val":"Created By"
},
{
"content":"555555000000012347",
"val":"MODIFIEDBY"
},
{
"content":"Doris Doe",
"val":"Modified By"
},
{
"content":"2013-06-14 17:24:10",
"val":"Created Time"
},
{
"content":"2013-06-14 17:24:10",
"val":"Modified Time"
},
{
"content":"2013-06-14 17:28:05",
"val":"Last Activity Time"
}
]
},
{
...
}
]
}
},
"uri":"/crm/private/json/Contacts/getRecords"
}
}
Here is how my Object looks:
public class Contact
{
[JsonProperty(PropertyName = "CONTACTID")]
public string ContactID { get; set; }
[JsonProperty(PropertyName = "SMOWNERID")]
public string OwnerID { get; set; }
[JsonProperty(PropertyName = "Contact Owner")]
public string ContactOwner { get; set; }
[JsonProperty(PropertyName = "First Name")]
public string FirstName { get; set; }
[JsonProperty(PropertyName = "Last Name")]
public string LasName { get; set; }
[JsonProperty(PropertyName = "Email")]
public string Email { get; set; }
[JsonProperty(PropertyName = "SMCREATORID")]
public string CreatorID { get; set; }
[JsonProperty(PropertyName = "Created By")]
public string CreatedBy { get; set; }
[JsonProperty(PropertyName = "MODIFIEDBY")]
public string ModifiedByID { get; set; }
[JsonProperty(PropertyName = "Modified By")]
public string ModifiedBy { get; set; }
[JsonProperty(PropertyName = "Created Time")]
public DateTime CreatedTime { get; set; }
[JsonProperty(PropertyName = "Modified Time")]
public DateTime ModifiedTime { get; set; }
[JsonProperty(PropertyName = "Last Activity Time")]
public DateTime LastActivityTime { get; set; }
}
The "row" pattern repeats (no 1, 2, 3 ...) so what I am basically trying to get is a Generic List of Objects of this type. I am trying to using JSON.NET, but I am open to other suggestions if it makes this any easier.
This doesn't work in this case obviously:
var response = JsonConvert.DeserializeObject<Contact>(jsonString);
And neither does this:
var deserializedObjects = JsonConvert.DeserializeObject<List<Contact>>(jsonString);
Here is a workaround I have put together to parse this using JavaScriptSerializer, but it is by far one of my worst code blocks ever!
List<Contact> loContactList = new List<Contact>();
Contact loContact = null;
Dictionary<string, object> dictionary = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(jsonString);
var response = (Dictionary<string, object>)dictionary["response"];
var result = (Dictionary<string, object>)response["result"];
var contacts = (Dictionary<string, object>)result["Contacts"];
var row = (ArrayList)contacts["row"];
foreach (var item in row)
{
var loArrayItem = (Dictionary<string, object>)item;
var fl = (ArrayList)loArrayItem["FL"];
loContact = new Contact();
foreach (var contactitem in fl)
{
var contactdict = (Dictionary<string, object>)contactitem;
string val = (string)contactdict["val"];
string content = (string)contactdict["content"];
if (val == "CONTACTID")
{
loContact.ContactID = content;
}
else if (val == "SMOWNERID")
{
loContact.OwnerID = content;
}
else if (val == "Contact Owner")
{
loContact.ContactOwner = content;
}
else if (val == "First Name")
{
loContact.FirstName = content;
}
else if (val == "Last Name")
{
loContact.LastName = content;
}
else if (val == "Email")
{
loContact.Email = content;
}
else if (val == "SMCREATORID")
{
loContact.CreatorID = content;
}
else if (val == "Created By")
{
loContact.CreatedBy = content;
}
else if (val == "MODIFIEDBY")
{
loContact.ModifiedByID = content;
}
else if (val == "Modified By")
{
loContact.ModifiedBy = content;
}
else if (val == "Created Time")
{
loContact.CreatedTime = Convert.ToDateTime(content);
}
else if (val == "Modified Time")
{
loContact.ModifiedTime = Convert.ToDateTime(content);
}
else if (val == "Last Activity Time")
{
loContact.LastActivityTime = Convert.ToDateTime(content);
}
}
loContactList.Add(loContact);
}
I have gone through other similar posts on StackOverflow and none of them seem to provide a solution for this problem. Does anyone have a solution for this? My goal is to parse this JSON response in a more elegant way, which doesn't involve a million dictionary objects and ArrayList! Any help would be appreciated.
Thanks,
Pete
Update 7/2/13:
Based on Manvik's suggestion, I put together the following additional solution:
public class ResponseActual
{
[JsonProperty("response")]
public Response2 Response { get; set; }
}
public class Response2
{
[JsonProperty("result")]
public Result Result { get; set; }
[JsonProperty("uri")]
public string Uri { get; set; }
}
public class Result
{
[JsonProperty("Contacts")]
public Contacts Contacts { get; set; }
}
public class Contacts
{
[JsonProperty("row")]
public IList<Row> Row { get; set; }
}
public class Row
{
[JsonProperty("no")]
public string No { get; set; }
[JsonProperty("FL")]
public IList<FL> FL { get; set; }
}
public class FL
{
[JsonProperty("content")]
public string Content { get; set; }
[JsonProperty("val")]
public string Val { get; set; }
}
List<Contact> loContactList = new List<Contact>();
Contact loContact = null;
ResponseActual respone = JsonConvert.DeserializeObject<ResponseActual>(jsonString);
foreach (var row in respone.Response.Result.Contacts.Row)
{
loContact = new Contact();
var rowItem = row.FL.ToList();
try { loContact.ContactID = rowItem.Where<FL>((s, t) => s.Val == "CONTACTID").Select(x => x.Content).Single(); }
catch { }
try { loContact.OwnerID = rowItem.Where<FL>((s, t) => s.Val == "SMOWNERID").Select(x => x.Content).Single(); }
catch { }
try { loContact.ContactOwner = rowItem.Where<FL>((s, t) => s.Val == "Contact Owner").Select(x => x.Content).Single(); }
catch { }
try { loContact.FirstName = rowItem.Where<FL>((s, t) => s.Val == "First Name").Select(x => x.Content).Single(); }
catch { }
try { loContact.LastName = rowItem.Where<FL>((s, t) => s.Val == "Last Name").Select(x => x.Content).Single(); }
catch { }
try { loContact.Email = rowItem.Where<FL>((s, t) => s.Val == "Email").Select(x => x.Content).Single(); } catch { }
try { loContact.CreatorID = rowItem.Where<FL>((s, t) => s.Val == "SMCREATORID").Select(x => x.Content).Single(); }
catch { }
try { loContact.CreatedBy = rowItem.Where<FL>((s, t) => s.Val == "Created By").Select(x => x.Content).Single(); }
catch { }
try { loContact.ModifiedByID = rowItem.Where<FL>((s, t) => s.Val == "MODIFIEDBY").Select(x => x.Content).Single(); }
catch { }
try { loContact.ModifiedBy = rowItem.Where<FL>((s, t) => s.Val == "Modified By").Select(x => x.Content).Single(); }
catch { }
try { loContact.CreatedTime = Convert.ToDateTime(rowItem.Where<FL>((s, t) => s.Val == "Created Time").Select(x => x.Content).Single()); }
catch { }
try { loContact.ModifiedTime = Convert.ToDateTime(rowItem.Where<FL>((s, t) => s.Val == "Modified Time").Select(x => x.Content).Single()); }
catch { }
try { loContact.LastActivityTime = Convert.ToDateTime(rowItem.Where<FL>((s, t) => s.Val == "Last Activity Time").Select(x => x.Content).Single()); }
catch { }
loContactList.Add(loContact);
}

Use the below classes for de-serializing using JSON.Net
public class ResponseActual
{
[JsonProperty("response")]
public Response2 Response { get; set; }
}
public class Response2
{
[JsonProperty("result")]
public Result Result { get; set; }
[JsonProperty("uri")]
public string Uri { get; set; }
}
public class Result
{
[JsonProperty("Contacts")]
public Contacts Contacts { get; set; }
}
public class Contacts
{
[JsonProperty("row")]
public IList<Row> Row { get; set; }
}
public class Row
{
[JsonProperty("no")]
public string No { get; set; }
[JsonProperty("FL")]
public IList<FL> FL { get; set; }
}
public class FL
{
[JsonProperty("content")]
public string Content { get; set; }
[JsonProperty("val")]
public string Val { get; set; }
}
//To De-serialize
ResponseActual respone = JsonConvert.DeserializeObject<ResponseActual>(jSON_sTRING)
//Get the contacts list
List<FL> contacts = respone.Response.Result.Contacts.Row[0].FL.ToList();
//Now Get the required value using LINQ
var value = contacts.Where<FL>((s, e) => s.Val =="Email").Select(x=>x.Content).Single();
You may also checkout this -
Deserializing JSON to .NET object using Newtonsoft (or LINQ to JSON maybe?)

You need to include the following portion of JSON in your deserialization object Type :
{
"response":
{
"result":
{
"Contacts":
{
"row":
[
{
"no":"1",
"FL":
Class type : 'Contact' is inadequate.

you can use this code:
dynamic dictionary = (JsonConvert.DeserializeObject>(jsonstring))["response"];
var result = dictionary.result;
var contact= result.Contacts;
var row= contact.row;
foreach (var item in row)
{
var no= item.no;
}

Can I ask you something? Are you the one exporting the JSON? I ask this because the format is quite weird and it does get in the way of your code.
A simpler format would allow you to serialize the string pretty much in a direct way.

Related

Unable to fetch data from Azure Cosmos DB

I am having the below model class CallbackResponse.cs :
public class CallbackResponse
{
public Callback Data { get; set; }
}
public class Callback
{
public IEnumerable<ReviewInProgressActivityFeed> ActivitiesFeed { get; set; }
}
public class ReviewInProgressActivityFeed
{
public ReviewInProgressStatus ReviewerSession { get; set; }
}
public class ReviewInProgressStatus
{
public Guid ReviewActivityId { get; set; }
public string ReviewerName { get; set; }
public string ReviewComments { get; set; }
public DateTime ActivityDateTime { get; set; }
}
Sample Payload:
{
"data":
{
"activitiesFeed": [
{
"reviewerSession":
{
"reviewActivityId": "dd9937c3-7c01-4a4a-bc8d-05ef37b07ee5",
"ReviewerName": "Verification Team",
"reviewComments": "upload business verification document for further verification.",
"activityDateTime": "2021-03-31T18:34:26.5978962Z"
},
},
{
"reviewerSession":
{
"reviewActivityId": "dd9937c3-7c01-4a4a-bc8d-05ef37b07ee5",
"ReviewerName": "Other Team",
"reviewComments": "other documents required for verification.",
"activityDateTime": "2021-03-31T19:34:26.5978962Z"
},
}
]
}
}
I am trying to get the data from DB via the CallbackResponse model class. Please find the code for below.
public async Task<CallbackResponse> CallbackActivityFeedAsync(Guid Id)
{
CallbackResponse containerItems = new CallbackResponse();
IQueryable<HumanReviewRequest> query = cosmosReviewRequestContainer.GetItemLinqQueryable<HumanReviewRequest>(true);
query = query.Where(x => x.id == Id);
FeedIterator<HumanReviewRequest> feedIterator = query.ToFeedIterator();
while (feedIterator.HasMoreResults)
{
FeedResponse<HumanReviewRequest> r = await feedIterator.ReadNextAsync().ConfigureAwait(false);
foreach (HumanReviewRequest requestModel in r)
{
containerItems = new CallbackResponse
{
Data = new Callback
{
ActivitiesFeed = new List<ReviewInProgressActivityFeed>
{
new ReviewInProgressActivityFeed
{
ReviewerSession = new ReviewInProgressStatus
{
ReviewActivityId = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewActivityId).LastOrDefault(),
ActivityDateTime = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ActivityDateTime).LastOrDefault(),
ReviewComments = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewerComments).LastOrDefault(),
ReviewerName = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewerName).LastOrDefault()
}
}
}
}
};
}
}
return containerItems;
}
The Problem here is I could not able to fetch all the records present in activitiesFeed array in DB. Instead I could only able to fetch the last record in that array(I am using Azure Cosmos DB). Please help me in this.
HumanReviewRequest.cs (which is DB Class)for reference:
public class HumanReviewRequest
{
public Guid Id { get; set; }
public IEnumerable<ReviewActivity> ReviewActivities { get; set; }
public class ReviewActivity
{
public ReviewerSession ReviewerSession { get; set; }
public string UserComments { get; set; }
}
public class ReviewerSession
{
public Guid ReviewActivityId { get; set; }
public Guid ReviewerUserId { get; set; }
public DateTime ActivityDateTime { get; set; }
public string ReviewerComments { get; set; }
}
This is because you are creating a new List<ReviewInProgressActivityFeed>, a new ReviewInProgressActivityFeed and reading only the last element in activitiesFeed for each iteration:
ReviewerSession = new ReviewInProgressStatus
{
ReviewActivityId = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewActivityId).LastOrDefault(),
ActivityDateTime = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ActivityDateTime).LastOrDefault(),
ReviewComments = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewerComments).LastOrDefault(),
ReviewerName = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewerName).LastOrDefault()
}
Try below code instead of the above code:
public async Task<CallbackResponse> CallbackActivityFeedAsync(Guid Id)
{
CallbackResponse containerItems = new CallbackResponse();
containerItems.Data = new Callback();
containerItems.Data.ActivitiesFeed = new List<ReviewInProgressActivityFeed>();
IQueryable<HumanReviewRequest> query = cosmosReviewRequestContainer.GetItemLinqQueryable<HumanReviewRequest>(true);
query = query.Where(x => x.id == Id);
FeedIterator<HumanReviewRequest> feedIterator = query.ToFeedIterator();
while (feedIterator.HasMoreResults)
{
FeedResponse<HumanReviewRequest> r = await feedIterator.ReadNextAsync().ConfigureAwait(false);
if (r != null || r.ReviewActivities != null)
{
foreach (HumanReviewRequest requestModel in r.ReviewActivities)
{
containerItems.Data.ActivitiesFeed.Add(
new ReviewInProgressActivityFeed
{
ReviewerSession = new ReviewInProgressStatus
{
ReviewActivityId = requestModel.ReviewerSession.ReviewActivityId,
ActivityDateTime = requestModel.ReviewerSession.ActivityDateTime,
ReviewComments = requestModel.ReviewerSession.ReviewerComments,
ReviewerName = requestModel.ReviewerSession.ReviewerName
}
});
}
}
}
};
}
}
return containerItems;
}

Firebase database timestamp is not serializing, getting error {"Unexpected character encountered while parsing value: {. Path 'date',

i want to retrieve timestamp data of firestore collection, but im getting error - {"Unexpected character encountered while parsing value: {. Path 'date',
date field data screenshot
data screenshot
sample data set
{"Tyres":"12","sr_no":"","TruckNumber":"TS 12345","image_url":"https://firebasestorage.googleapis.com/v0/b/truck-41c31.appsp.jpg?alt=media&token=2dc86208-7f71-4e3c-876c-70a80d4822bf","date":{}}
code
Query truckQuery = fireStoreDb.Collection("users/QRRsqyWUas/orders");
QuerySnapshot truckQuerySnapshot = await truckQuery.GetSnapshotAsync();
List<Truck> lstTruck = new List<Truck>();
foreach (DocumentSnapshot documentSnapshot in truckQuerySnapshot.Documents)
{
if (documentSnapshot.Exists)
{
Dictionary<string, object> city = documentSnapshot.ToDictionary();
string json = JsonConvert.SerializeObject(city);
Truck newtruck = JsonConvert.DeserializeObject<Truck>(json);
newtruck.TruckNumber = documentSnapshot.Id;
newtruck.date = documentSnapshot.CreateTime.Value.ToDateTime();
lstTruck.Add(newtruck);
}
}
model
public class Truck
{
[FirestoreProperty]
[Required]
public string TruckNumber { get; set; }
[FirestoreProperty]
[Required]
public string Tyres { get; set; }
public DateTime date { get; set; }
[FirestoreProperty]
[Required]
public string image_url { get; set; }
[FirestoreProperty]
[Required]
public string sr_no { get; set; }
}
firestore date field data - 29 June 2020 at 22:23:44 UTC+5:30
foreach (DocumentSnapshot documentSnapshot in truckQuerySnapshot.Documents)
{
if (documentSnapshot.Exists)
{
Dictionary<string, object> city = documentSnapshot.ToDictionary();
city["date"] = DateTime.Now.ToString();//modified here for resolve
string json = JsonConvert.SerializeObject(city);
Truck newtruck = JsonConvert.DeserializeObject<Truck>(json);
newtruck.Docid = documentSnapshot.Id;
newtruck.date = documentSnapshot.CreateTime.Value.ToDateTime();
lstTruck.Add(newtruck);
}
}
i have assigned datetime now before the serializeObject(city), and my error gone.
I face the same Error in .netcore 5 with firebase Database
Solution :
Change in property : DateTime To DateTimeOffset
ex... "**public DateTimeOffset date { get; set; }**"
And in databse insert Date always in utc
ex... **object.date = DateTime.UtcNow;**
I Hope this helps everyone....
I face this problem and I solve it
I don't know if this consider a solution but I will share it here
Also, I use this method to convert returned Dictionary to Observablecollection Model
using Google.Cloud.Firestore;
FirestoreDb fdb;
private ObservableCollection<Items_Model> _result = new ObservableCollection<Items_Model>();
public ObservableCollection<Items_Model> result
{
get { return _result; }
}
public Page_Items()
{
InitializeComponent();
MyListView.ItemsSource = result;
GetAllItems();
}
Items_Model
using System;
using System.ComponentModel;
using System.Text.Json.Serialization;
namespace WPFApp1.Models
{
public class Items_Model : INotifyPropertyChanged
{
private string _Name { get; set; }
public Guid id { get; set; }
public string Name
{
get { return _Name; }
set { _Name = value; NotifyPropertyChanged("Name"); }
}
public string iid { get; set; }
public string Barcode { get; set; }
public string ImageUrl { get; set; }
public string Section { get; set; }
public string Notes { get; set; }
[JsonIgnore]
public DateTime AddDate { get; set; }
[JsonIgnore]
public DateTime UpdateDate { get; set; }
public string AddBy { get; set; }
public string UpdateBy { get; set; }
[JsonIgnore]
public string Key { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Here is my solution
public async void GetAllItems()
{
try
{
CollectionReference customersRef = fdb.Collection("Items");
FirestoreChangeListener listener = customersRef.Listen(snapshot =>
{
foreach (var change in snapshot.Changes)
{
var dic = change.Document.ToDictionary();
//Here I Convert to DateTime
string AddD = dic["AddDate"].ToString();
string stringAddDate = AddD.Split(new string[] { "Timestamp: " }, StringSplitOptions.None).Last();
DateTime AddDate = Convert.ToDateTime(stringAddDate);
string UpD = dic["UpdateDate"].ToString();
string stringUpdateDate = UpD.Split(new string[] { "Timestamp: " }, StringSplitOptions.None).Last();
DateTime UpdateDate = Convert.ToDateTime(stringUpdateDate);
var obj = new Items_Model();
obj.id = Guid.Parse(dic["id"].ToString());
obj.iid = (dic["iid"] == null ? "" : dic["iid"].ToString());
obj.Name = (dic["Name"] == null ? "" : dic["Name"].ToString()) ;
obj.Barcode = (dic["Barcode"] == null ? "" : dic["Barcode"].ToString()) ;
obj.ImageUrl = (dic["ImageUrl"] == null ? "" : dic["ImageUrl"].ToString());
obj.Section = (dic["Section"] == null ? "" : dic["Section"].ToString());
obj.Notes = (dic["Notes"] == null ? "" : dic["Notes"].ToString());
obj.AddDate = AddDate;
obj.UpdateDate = UpdateDate;
obj.AddBy = (dic["AddBy"] == null ? "" : dic["AddBy"].ToString());
obj.UpdateBy = (dic["UpdateBy"] == null ? "" : dic["UpdateBy"].ToString());
if (change.ChangeType.ToString() == "Added")
{
App.Current.Dispatcher.Invoke((Action)delegate
{
result.Add(obj);
});
}
else if (change.ChangeType.ToString() == "Modified")
{
if (result.Where(c => c.id == obj.id).Any())
{
var item = result.Where(c => c.id == obj.id).FirstOrDefault();
//item = obj;
App.Current.Dispatcher.Invoke((Action)delegate
{
result.Insert(result.IndexOf(item), obj);
result.Remove(item);
});
}
}
else if (change.ChangeType.ToString() == "Removed")
{
if (result.Where(c => c.id == obj.id).Any())
{
var item = result.Where(c => c.id == obj.id).FirstOrDefault();
App.Current.Dispatcher.Invoke((Action)delegate
{
result.Remove(item);
});
}
}
}
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error");
}
}

FreshService API with Restsharp and Dynamic property names

I'm trying to use the FreshService API for assets and getting a list of assets returns JSON like the below. Notice that the properties inside "levelfield_values" have the id appended to the end of their property name.
I'm trying to pull the "license_expiry_date" for all assets but am having problems converting to objects to pull that field since the name changes with each item. Any suggestions?
{"config_item": {"agent_id": 215,
"asset_tag": batch_2017,
"assigned_on": "2014-07-18T03:54:18+05:30",
"ci_type_id": 3,
"created_at": "2014-07-25T14:25:04+05:30",
"deleted": false,
"department_id": 4,
"depreciation_id": null,
"description": null,
"disabled": false,
"display_id": 113,
"expiry_notified": false,
"id": 113,
"impact": 3,
"location_id": 21,
"name": "windows 7",
"salvage": null,
"trashed": false,
"updated_at": "2014-07-25T14:25:04+05:30",
"user_id": 214,
"department_name": "Finance",
"used_by": "Rachel",
"business_impact": "Medium",
"agent_name": "Andrea",
"levelfield_values": {
"product_3": 100,
"vendor_3": 43,
"cost_3": 4000,
"license_validity_3": 24,
"installation_date_3": "2014-07-25T14:25:04+05:30",
"license_expiry_date_3": "2016-07-25T00:00:00+05:30",
"license_key_3": "234_423_543_534",
"version_3": 2,
"license_type_3": "commercial",
"installed_machine_3": "Andrea’s computer",
"installation_path_3": null,
"last_audit_date_3": "2014-07-25T14:25:04+05:30"
},
"ci_type_name": "Software",
"product_name": "windows_os",
"vendor_name": "micosoft",
"state_name": null,
"location_name": "America" } }
You could do something like this
JsonConvert.DeserializeObject<JObject>(jsonString)["config_item"]["levelfield_values"]
.ToObject<JObject>()
.Properties()
.FirstOrDefault(x => x.Name.ToLower().Contains("license_validity"));
basically you;
navigate into config_item
navigate into levelfield_values
get all properties of navigated object (levelfield_values)
find a property that has a name like license_validity
That example Json is awful and incorrect, but.. here is how I'll make serialization seamless.
levelfield_values to definied as Expando object
defining a class for levelfield_values clean (no id in properties)
Add regenerative properties between Expando to defined class property and viceversa
I used visual studio paste special to write initially the class.
Example or fiddle:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var example = new Config
{
config_item = new Config_Item
{
LevelfieldValuesParshed = new CleanLevelfield_Values
{
Id = 2,
product = 1232
}
}
};
var serialized = JsonConvert.SerializeObject(example); // should have "product_2" : 2 inside
// VOILA deserialization with property parsing in a clean object
var deserialzed = JsonConvert.DeserializeObject<Config>(serialized);
if (example.config_item.LevelfieldValuesParshed.Id != deserialzed.config_item.LevelfieldValuesParshed.Id ||
example.config_item.LevelfieldValuesParshed.product != deserialzed.config_item.LevelfieldValuesParshed.product)
{
throw new Exception("Impossible to happen!!!");
}
}
}
public class Config
{
public Config_Item config_item { get; set; }
}
public class Config_Item
{
public int agent_id { get; set; }
public string asset_tag { get; set; }
public DateTime assigned_on { get; set; }
public int ci_type_id { get; set; }
public DateTime created_at { get; set; }
public bool deleted { get; set; }
public int department_id { get; set; }
public object depreciation_id { get; set; }
public object description { get; set; }
public bool disabled { get; set; }
public int display_id { get; set; }
public bool expiry_notified { get; set; }
public int id { get; set; }
public int impact { get; set; }
public int location_id { get; set; }
public string name { get; set; }
public object salvage { get; set; }
public bool trashed { get; set; }
public DateTime updated_at { get; set; }
public int user_id { get; set; }
public string department_name { get; set; }
public string used_by { get; set; }
public string business_impact { get; set; }
public string agent_name { get; set; }
// Regenerative property with backing filed => _levelfieldValuesParshed
[JsonIgnore] // Ignore property at serialization
public CleanLevelfield_Values LevelfieldValuesParshed
{
get
{
if (_levelfieldValuesParshed == null)
{
if (_levelfield_values != null) // if null everything is null
{
var propsByName = (IDictionary<string, object>)_levelfield_values; // Expando Object to dictionary
var product = propsByName.Keys.FirstOrDefault(x => x.StartsWith("product_", StringComparison.InvariantCultureIgnoreCase)); // user first to fail if not found, it can be smarter but it works
if (!string.IsNullOrEmpty(product))// hurray we know the id
{
if (int.TryParse(product.Replace("product_", ""), out int id)) // C# 7
{
// Cleaner code can be written (generic method to set get object props with reflection)
_levelfieldValuesParshed = new CleanLevelfield_Values
{
Id = id
};
_levelfieldValuesParshed.product = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"product_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"product_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.vendor = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"vendor_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"vendor_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.cost = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"cost_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"cost_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.license_validity = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"license_validity_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"license_validity_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.installation_date = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"installation_date_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToDateTime(propsByName.First(x => x.Key.Equals($"installation_date_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : DateTime.MinValue;
_levelfieldValuesParshed.license_expiry_date = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"license_expiry_date_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToDateTime(propsByName.First(x => x.Key.Equals($"license_expiry_date_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : DateTime.MinValue;
_levelfieldValuesParshed.license_key = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"license_key_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToString(propsByName.First(x => x.Key.Equals($"license_key_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : string.Empty;
_levelfieldValuesParshed.version = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"version_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"version_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.license_type = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"license_type_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToString(propsByName.First(x => x.Key.Equals($"license_type_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : string.Empty;
_levelfieldValuesParshed.installed_machine = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"installed_machine_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToString(propsByName.First(x => x.Key.Equals($"installed_machine_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : string.Empty;
_levelfieldValuesParshed.installation_path = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"installation_path_{id}", StringComparison.InvariantCultureIgnoreCase)))
? propsByName.First(x => x.Key.Equals($"installation_path_{id}", StringComparison.InvariantCultureIgnoreCase)).Value : new object();
_levelfieldValuesParshed.last_audit_date = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"last_audit_date_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToDateTime(propsByName.First(x => x.Key.Equals($"last_audit_date_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : DateTime.MinValue;
}
}
}
}
return _levelfieldValuesParshed;
}
set
{
_levelfieldValuesParshed = value;
_levelfield_values = null;
}
}
private CleanLevelfield_Values _levelfieldValuesParshed;
// Regenerative Expando property with backing field => _levelfield_values
public System.Dynamic.ExpandoObject levelfield_values
{
get
{
if (_levelfieldValuesParshed != null)
{
_levelfield_values = new ExpandoObject();
// Cleaner code can be written with a foreach (generic method to set get object props with reflection)
var keValuesPairs = (IDictionary<string, object>)_levelfield_values;
keValuesPairs.Add($"product_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.product);
keValuesPairs.Add($"vendor_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.vendor);
keValuesPairs.Add($"cost_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.cost);
keValuesPairs.Add($"license_validity_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.license_validity);
keValuesPairs.Add($"installation_date_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.installation_date);
keValuesPairs.Add($"license_expiry_date_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.license_expiry_date);
keValuesPairs.Add($"license_key_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.license_key);
keValuesPairs.Add($"version_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.version);
keValuesPairs.Add($"license_type_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.license_type);
keValuesPairs.Add($"installed_machine_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.installed_machine);
keValuesPairs.Add($"installation_path_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.installation_path);
keValuesPairs.Add($"last_audit_date_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.last_audit_date);
return _levelfield_values;
}
return null;
}
set
{
_levelfield_values = value;
_levelfieldValuesParshed = null; // remove cleaned object, it will regenerated itself when opened
}
}
private ExpandoObject _levelfield_values;
public string ci_type_name { get; set; }
public string product_name { get; set; }
public string vendor_name { get; set; }
public object state_name { get; set; }
public string location_name { get; set; }
}
public class CleanLevelfield_Values
{
public int Id { get; set; }
public int product { get; set; }
public int vendor { get; set; }
public int cost { get; set; }
public int license_validity { get; set; }
public DateTime installation_date { get; set; }
public DateTime license_expiry_date { get; set; }
public string license_key { get; set; }
public int version { get; set; }
public string license_type { get; set; }
public string installed_machine { get; set; }
public object installation_path { get; set; }
public DateTime last_audit_date { get; set; }
}
}

Restsharp error on deserialzation when nested value is null

I'm running into an error when a nested value is null. If the value is not null everything works as expected. This does not happen if the value is not nested.
The error is:
InvalidCastException: Unable to cast object of type 'System.String' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
The error happens when I'm checking response.ErrorException != null on the List Contract
Json returned: Contract administrator is nested and blank error: ends is not nested blank and no error:
"result": [
{
"sys_id": "06dc3133db1747808c47499e0b96192e",
"number": "CNTR001234",
"short_description": "Contract 123",
"u_internal_contact": {
"link": "https://website",
"value": "5b4080490a0a3c9e016cb2a9f4eb57b1"
},
"vendor": {
"link": "https://website",
"value": "b7e7c073c0a801690143e7b7d29eb408"
},
"ends": "",
"payment_amount": "60000",
"u_status": "Active",
"starts": "2018-01-01",
"contract_administrator": ""
}
]
}
Code
public class Results
{
public List<Contract> items { get; set; }
}
public class Contract
{
public string sys_id { get; set; }
public string number { get; set; }
public string short_description { get; set; }
public string ends { get; set; }
public string payment_amount { get; set; }
public string u_status { get; set; }
public string starts { get; set; }
public Vendor vendor { get; set; }
public ContractAdmin contract_administrator { get; set; }
public InternalContact u_internal_contact { get; set; }
}
public class Vendor
{
public string link { get; set; }
public string value { get; set; }
}
public class ContractAdmin
{
public string link { get; set; }
public string value { get; set; }
}
public class InternalContact
{
public string link { get; set; }
public string value { get; set; }
}
public class refResults
{
public List<refName> itemName { get; set; }
}
public class refName
{
public string name { get; set; }
}
class ImportContracts
{
public static void ProcessImport()
{
RestClient contractsRequest = new RestClient(Properties.Settings.Default.RestURL);
contractsRequest.Authenticator = new HttpBasicAuthenticator(Properties.Settings.Default.userName, Properties.Settings.Default.password);
contractsRequest.AddHandler("application/json", new RestSharp.Deserializers.JsonDeserializer());
RestRequest request = new RestRequest();
request.RootElement = "result";
request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
IRestResponse<List<Contract>> response = contractsRequest.Execute<List<Contract>>(request);
Console.WriteLine(response.Content);
if (response.ErrorException != null)
{
const string message = "Error retrieving response. Check inner details for more info.";
var ex = new ApplicationException(message, response.ErrorException);
throw ex;
}
foreach (Contract contract in response.Data)
{
//Console.WriteLine(contract.sys_id);
string strVendor = GetName(contract.vendor.link.ToString());
string strInternalContact = GetName(contract.u_internal_contact.link.ToString());
string strContractAdmin = GetName(contract.contract_administrator.ToString());
}
}
static public string GetName (string link)
{
RestClient nameRequest = new RestClient(link);
nameRequest.Authenticator = new HttpBasicAuthenticator(Properties.Settings.Default.userName, Properties.Settings.Default.password);
nameRequest.AddHandler("application/json", new RestSharp.Deserializers.JsonDeserializer());
RestRequest requestedName = new RestRequest();
requestedName.RootElement = "result";
requestedName.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
IRestResponse<List<refName>> response = nameRequest.Execute<List<refName>>(requestedName);
if (response.ErrorException != null)
{
const string message = "Error retrieving response. Check inner details for more info.";
var ex = new ApplicationException(message, response.ErrorException);
throw ex;
}
foreach (refName refname in response.Data)
{
return refname.name;
}
return "name not found";
}
}
Any help would be appreciated!
Looking at your JSON, "contract_administrator" is not null, it's an empty string. Your contract requires a ContractAdmin object, so what it's likely doing is attempting to cast an empty string to a ContractAdmin.
If you change "contract_administrator" to be null instead of an empty string, I'm willing to bet that it will parse correctly.

Deserializing Json with an index

I try to deserialize a JSon answer from a webservice, but I don't know how do this.
There is an example :
{
"0": {
"course_info": {
"course_id": 3,
"code": "",
"course_name": "Fiches Docebo",
"course_description": "<p>Fiches pratiques Docebo<\/p>",
"status": "2",
"selling": "0",
"price": "",
"subscribe_method": "2",
"course_edition": "0",
"course_type": "elearning",
"sub_start_date": "",
"sub_end_date": "",
"date_begin": "0000-00-00",
"date_end": "0000-00-00",
"course_link": ""
}
},
"1": {
"course_info": {
"course_id": 5,
"code": "prout",
"course_name": "Prout",
"course_description": "<p style=\"text-align: justify;\">Prout<\/p>",
"status": "2",
"selling": "0",
"price": "",
"subscribe_method": "2",
"course_edition": "0",
"course_type": "elearning",
"sub_start_date": "",
"sub_end_date": "",
"date_begin": "0000-00-00",
"date_end": "0000-00-00",
"course_link": ""
}
},
"success": true
}
The first node is an index, and I don't succeed to deserialize this without declarate one by one numbers...
I try like this :
public class ListCoursesResponse
{
public bool? success { get; set; }
public Dictionary<String,Course> courses { get; set; }
}
but It doesn't work.
I don't know how explain to RestSharp first nodes are numbers which are 1, 2, 3 to the last node...
Maybe I need to code a specific deserializer, but I don't know how..
I'm using RestSharp deserializer.
EDIT : there is Course class
[DeserializeAs(Name = "course_info")]
public class Course
{
[DeserializeAs(Name = "course_id")]
public long? Id { get; set; }
[DeserializeAs(Name = "course_name")]
public string Name { get; set; }
public string Code { get; set; }
[DeserializeAs(Name = "course_description")]
public string Description { get; set; }
[DeserializeAs(Name = "course_edition")]
public Boolean? IsEditable { get; set; }
[DeserializeAs(Name = "course_link")]
public string Url { get; set; }
[DeserializeAs(Name = "course_type")]
public string Type { get; set; }
[DeserializeAs(Name = "date_begin")]
public string BeginDate { get; set; }
[DeserializeAs(Name = "date_end")]
public string EndDate { get; set; }
public string Price { get; set; }
[DeserializeAs(Name = "selling")]
public Boolean? IsSalable { get; set; }
public int? Status { get; set; }
[DeserializeAs(Name = "sub_start_date")]
public string SubscriptionStartDate { get; set; }
[DeserializeAs(Name = "sub_end_date")]
public string SubscriptionStopDate { get; set; }
[DeserializeAs(Name = "subscribe_method")]
public int? SubscriptionMethod { get; set; }
}
EDIT 2 : There is how I call the library
public ListCoursesResponse ListCourses(int? categoryId = null)
{
if (categoryId != null)
{
List<KeyValuePair<String, Object>> list = new List<KeyValuePair<string, object>>();
list.Add(new KeyValuePair<String, Object>("category", categoryId));
return Execute<ListCoursesResponse>(String.Format("{0}/{1}", _course, "listCourses"), list.ToArray());
}
return Execute<ListCoursesResponse>(String.Format("{0}/{1}", _course, "listCourses"));
}
public T Execute<T>(string resource, params KeyValuePair<String, Object>[] parameters) where T : new()
{
var client = new RestClient();
client.AddHandler("text/html", new RestSharp.Deserializers.JsonDeserializer());
client.BaseUrl = DoceboApiUrl;
var requestUrl = DoceboApiUrl;
if (!requestUrl.EndsWith("/"))
requestUrl += "/";
requestUrl += resource;
var req = new RestRequest(RestSharp.Method.POST);
req.Resource = resource;
// Parameters Management
if (parameters != null)
foreach (KeyValuePair<String, Object> kvp in parameters)
req.AddParameter(new Parameter() { Name = kvp.Key, Value = kvp.Value, Type = ParameterType.GetOrPost });
client.Authenticator = new DoceboAuthenticator(ApiKey, ApiSecret, req.Parameters);
var response = client.Execute<T>(req);
if (response.ErrorException != null)
{
const string message = "Error retrieving response. Check inner details for more info.";
throw new ApplicationException(message, response.ErrorException);
}
return response.Data;
}
Proxy classes (ListCoursesResponse is writed as you wanted, rest is generated from json with json2csharp.com):
public class ListCoursesResponse
{
public bool? success { get; set; }
public Dictionary<String, Course> courses { get; set; }
}
public class Course
{
public CourseInfo course_info { get; set; }
}
public class CourseInfo
{
public int course_id { get; set; }
public string code { get; set; }
public string course_name { get; set; }
public string course_description { get; set; }
public string status { get; set; }
public string selling { get; set; }
public string price { get; set; }
public string subscribe_method { get; set; }
public string course_edition { get; set; }
public string course_type { get; set; }
public string sub_start_date { get; set; }
public string sub_end_date { get; set; }
public string date_begin { get; set; }
public string date_end { get; set; }
public string course_link { get; set; }
}
Deserialization:
var parsed = JObject.Parse(input);
var result = new
ListCoursesResponse
{
success = parsed["success"].Value<bool?>(),
courses =
parsed.Properties()
.Where(prop => prop.Name != "success")
.ToDictionary(prop => prop.Name,
prop => prop.Value.ToObject<Course>())
};
I'm sure it's the worst way to manage this request, but here is my disgusting solution :
public ListCoursesResponse ListCourses(int? categoryId = null)
{
Dictionary<String, Object> dico = null;
if (categoryId != null)
{
List<KeyValuePair<String, Object>> list = new List<KeyValuePair<string, object>>();
list.Add(new KeyValuePair<String, Object>("category", categoryId));
dico = Execute<Dictionary<String, Object>>(String.Format("{0}/{1}", _course, "listCourses"), list.ToArray());
}
dico = Execute<Dictionary<String, Object>>(String.Format("{0}/{1}", _course, "listCourses"));
return dicoToResponse(dico);
}
And :
private ListCoursesResponse dicoToResponse(Dictionary<String, Object> dico)
{
ListCoursesResponse coursesResponse = new ListCoursesResponse();
coursesResponse.success = (Boolean)dico.First(x => x.Key == "success").Value;
if (coursesResponse.success == true)
{
dico.Remove("success");
foreach (Dictionary<String, Object> v in dico.Values)
{
Course course = new Course();
Dictionary<String, Object> values = (Dictionary<String, Object>)v["course_info"];
foreach (String key in values.Keys)
{
switch (key)
{
case "course_id" :
course.Id = (long)values[key];
break;
case "code":
course.Code = (string)values[key];
break;
case "course_name" :
course.Name = (string)values[key];
break;
case "course_description" :
course.Description = (string)values[key];
break;
case "status" :
course.Status = (string)values[key];
break;
case "selling" :
course.IsSalable = (string)values[key] == "0" ? false : true;
break;
case "price" :
course.Price = (string)values[key];
break;
case "subscribe_method" :
course.SubscriptionMethod = (string)values[key];
break;
case "course_edition" :
course.IsEditable = (string)values[key] == "0" ? false : true;
break;
case "course_type" :
course.Type = (string)values[key];
break;
case "sub_start_date" :
course.SubscriptionStartDate = (string)values[key];
break;
case "sub_end_date" :
course.SubscriptionEndDate = (string)values[key];
break;
case "date_begin" :
course.BeginDate = (string)values[key];
break;
case "date_end" :
course.EndDate = (string)values[key];
break;
case "course_link" :
course.Url = (string)values[key];
break;
}
}
coursesResponse.courses.Add(course);
}
}
return coursesResponse;
}

Categories

Resources