Deserializing object with dynamic property won't work - c#

I'm having troubles with deserializing an object with a dynamic field, which will hold different api responses.
This is how it looks like:
public class ServiceCallResult
{
private dynamic _dataObject;
private Type _dataType;
public Type DataType
{
get
{
return _dataType;
}
set
{
_dataType = value;
}
}
public dynamic DataObject
{
get
{
return _dataObject;
}
set
{
_dataObject = value;
}
}
public string ErrorMessage { get; set; }
public bool Success { get; set; }
public ServiceCallResult()
{
}
public ServiceCallResult(Type type, dynamic obj)
{
DataType = type;
DataObject = obj;
Success = true;
}
}
And it could hold an object like this:
public class ApiPractitioner
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CompanyName { get; set; }
public int CVR { get; set; }
public string Address { get; set; }
public short ZipCode { get; set; }
public string City { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Description { get; set; }
public byte[] ProfileImage{ get; set; }
//[JsonIgnore]
//public List<ApiPractice> Practices { get; set; }
//[JsonIgnore]
//public List<ApiClient> Clients { get; set; }
}
But when deserializing with this code:
var req = new RestRequest("practitioner/createpractitioner", Method.POST);
req.RequestFormat = DataFormat.Json;
req.AddBody(prac);
var response = _client.Execute<ServiceCallResult>(req);
ServiceCallResult td = new JsonDeserializer().Deserialize<ServiceCallResult>(response);
It ends up with a "MissingMethodException" in mscorlib.dll, additional information is "An abstract class can't be created".
The response object has the json content but there's and exception like the one when I use the JsonDeserializer and I can't figure out what's going on, I've searched for hours on Google and tried different suggestions with no luck.
Anyone tried the same with a dynamic property?
I'm using RestSharp for the json handling.
Best regards
Benjamin
After a little enlightment from Michael I've changed my ServiceCallResult to this:
public class ServiceCallResult<T> where T : BaseApiResponse
{
public T DataObject { get; set; }
public string ErrorMessage { get; set; }
public bool Success { get; set; }
public ServiceCallResult()
{
}
public ServiceCallResult(T obj)
{
DataObject = obj;
Success = true;
}
}
And all my api responses inherits from BaseApiResponse.

Related

Problem trying to deserialize a json with a model

I am trying to deserialize a json (using Newtonsoft.Json), i created the classes, but in json code there is a variable that i don't know if it is corrected or i don't know how to deserialize.
I am getting a null exception on line : MessageBox.Show(root.matriculations._14000.name)
private void btnImportaDados_Click(object sender, EventArgs e)
{
string getDataUrl = "https://xxx.xxx.com/api/matriculations/get.json?entity_code=14000";
try
{
using (var webClient = new System.Net.WebClient())
{
var json = webClient.DownloadString(getDataUrl);
var root = JsonConvert.DeserializeObject<Rootobject>(json);
MessageBox.Show(root.matriculations._14000.name);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
public class Rootobject
{
public Matriculations matriculations { get; set; }
}
public class Matriculations
{
public _14000 _14000 { get; set; }
}
public class _14000
{
public string entity_code { get; set; }
public string name { get; set; }
public string street { get; set; }
public string local { get; set; }
public string postal_code { get; set; }
public string sub_postal_code { get; set; }
public string phone1 { get; set; }
public object phone2 { get; set; }
public string email1 { get; set; }
public object email2 { get; set; }
public string entity_code_2 { get; set; }
public string courseaction_ref { get; set; }
public string courseaction_id { get; set; }
public string course_code { get; set; }
public string course { get; set; }
public Billingorderplan[] billingorderplans { get; set; }
}
public class Billingorderplan
{
public string id { get; set; }
public string paid { get; set; }
public string date { get; set; }
public object obs { get; set; }
public object payment_doc_num { get; set; }
public string value { get; set; }
public string description { get; set; }
public string entity_code { get; set; }
public object paid_date { get; set; }
}
The result(json data) from the url is:
{"matriculations":{"14000":{"entity_code":"14000","name":"Fabio Danilson Sivone Antonio","street":"Rua Dr. Pereira Jardim, Bl. 3 - 25 - 4 B","local":"Luanda","postal_code":"2685","sub_postal_code":"093","phone1":"923810539","phone2":null,"email1":"fabiodanilson1#hotmail.com","email2":null,"entity_code_2":"9957","courseaction_ref":"EPCE_01","courseaction_id":"1828","course_code":"EPCE","course":"Especializa\u00e7\u00e3o Avan\u00e7ada em Investiga\u00e7\u00e3o de Crime Econ\u00f3mico [E-learning]","billingorderplans":[{"id":"298","paid":"0","date":"2020-05-06","obs":null,"payment_doc_num":null,"value":"0.00","description":"Inscri\u00e7\u00e3o","entity_code":"14000","paid_date":null},{"id":"299","paid":"0","date":"2019-11-21","obs":null,"payment_doc_num":null,"value":"0.00","description":"1\u00aa presta\u00e7\u00e3o","entity_code":"14000","paid_date":null},{"id":"300","paid":"0","date":"2019-12-08","obs":null,"payment_doc_num":null,"value":"0.00","description":"2\u00aa presta\u00e7\u00e3o","entity_code":"14000","paid_date":null},{"id":"301","paid":"0","date":"2020-01-08","obs":null,"payment_doc_num":null,"value":"0.00","description":"3\u00aa presta\u00e7\u00e3o","entity_code":"14000","paid_date":null},{"id":"302","paid":"0","date":"2020-02-08","obs":null,"payment_doc_num":null,"value":"0.00","description":"4\u00aa presta\u00e7\u00e3o","entity_code":"14000","paid_date":null},{"id":"303","paid":"0","date":"2020-03-08","obs":null,"payment_doc_num":null,"value":"0.00","description":"5\u00aa presta\u00e7\u00e3o","entity_code":"14000","paid_date":null},{"id":"304","paid":"0","date":"2020-04-08","obs":null,"payment_doc_num":null,"value":"0.00","description":"6\u00aa presta\u00e7\u00e3o","entity_code":"14000","paid_date":null},{"id":"305","paid":"0","date":"2020-05-08","obs":null,"payment_doc_num":null,"value":"0.00","description":"7\u00aa presta\u00e7\u00e3o","entity_code":"14000","paid_date":null},{"id":"306","paid":"0","date":"2020-06-08","obs":null,"payment_doc_num":null,"value":"0.00","description":"8\u00aa presta\u00e7\u00e3o","entity_code":"14000","paid_date":null},{"id":"16595","paid":"0","date":"2020-08-27","obs":null,"payment_doc_num":null,"value":"20.00","description":"EExt - Atividade","entity_code":"14000","paid_date":null},{"id":"16596","paid":"0","date":"2020-08-27","obs":null,"payment_doc_num":null,"value":"45.00","description":"EExt - Teste","entity_code":"14000","paid_date":null},{"id":"16597","paid":"0","date":"2020-08-27","obs":null,"payment_doc_num":null,"value":"20.00","description":"EExt - Atividade","entity_code":"14000","paid_date":null},{"id":"16598","paid":"0","date":"2020-08-27","obs":null,"payment_doc_num":null,"value":"45.00","description":"EExt - Teste","entity_code":"14000","paid_date":null},{"id":"16599","paid":"0","date":"2020-08-27","obs":null,"payment_doc_num":null,"value":"20.00","description":"EExt - Atividade","entity_code":"14000","paid_date":null},{"id":"16601","paid":"0","date":"2020-08-27","obs":null,"payment_doc_num":null,"value":"45.00","description":"EExt - Teste","entity_code":"14000","paid_date":null},{"id":"16600","paid":"0","date":"2020-08-27","obs":null,"payment_doc_num":null,"value":"20.00","description":"EExt - Atividade","entity_code":"14000","paid_date":null}]}}}
But that "14000" is a varible value depending from the parameter passed.
How can i do it?
Thank You.
Since 14000 cannot be a variable name you will have to resort to some manual intervention to capture that dynamic value. The cleanest way is to remove the Matriculations class and replace it with Dictionary<int, _14000> (or Dictionary<string, _14000>).
public class Rootobject
{
public Dictionary<int, _14000> matriculations { get; set; }
}
This will ensure that the key is captured correctly even if it is a number. You can read the object from the dictionary's values as such: root.matriculations.Values.First().name. (Remember to import System.Linq for using .First().)
For clarity you can rename _14000 to something more descriptive.
You do not have _14000 in returned JSON but 14000 under matriculations.

How to read sections of JSON document?

I have a JSON document and I want to access the details of the STATUS SECTION but it keeps returning null.
JSON Data is as shown:
{
"results":[
{
"messageId":"15712480583306574",
"to":"",
"from":"TestServer",
"sentAt":"2019-10-16T17:47:38.368+0000",
"doneAt":"2019-10-16T17:47:38.370+0000",
"smsCount":1,
"mccMnc":"null",
"price":{
"pricePerMessage":0.0,
"currency":"USD"
},
"status":{
"groupId":5,
"groupName":"REJECTED",
"id":8,
"name":"REJECTED_PREFIX_MISSING",
"description":"Number prefix missing"
},
"error":{
"groupId":0,
"groupName":"OK",
"id":0,
"name":"NO_ERROR",
"description":"No Error",
"permanent":false
}
}
]
}
C# Code is:
string JsonData = response.Content.ToString();
dynamic results = JsonConvert.DeserializeObject<dynamic>(JsonData);
var statuses = results.status;
foreach(var stat in statuses) {
string groupname = stat.groupName.Value;
string name = stat.name.Value;
string description = stat.description.Value;
}
It keeps returning null, How can I access these members? I am using Newtonsoft.
If you want to access the status object property you need to rewrite your whole code.
string JsonData = response.Content.ToString();
var input = JObject.Parse(str);
var results = input["results"].Children();
var status = results.First()["status"];
string groupname = status["groupName"].ToString();
string name = status["name"].ToString();
string description = status["description"].ToString();
Console.WriteLine(groupname);
Console.WriteLine(name);
Console.WriteLine(description);
The result in Console
REJECTED
REJECTED_PREFIX_MISSING
Number prefix missing
But I would rather use concrete class. You need to create multiple classes. Here is good example.
public class Envelope
{
public List<Item> Results { get; set; }
}
public class Item
{
public Status Status { get; set; }
}
public class Status
{
public int GroupId { get; set; }
public string GroupName { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
After that the usage is much simpler.
string JsonData = response.Content.ToString();
MyEnvelope envelope = JsonConvert.DeserializeObject<MyEnvelope>(JsonData);
var status = envelope.results[0].status;
Console.WriteLine(status.GroupName);
Console.WriteLine(status.Name);
Console.WriteLine(status.Description);
Finest Option: Create A model for the JSON.
public class Price
{
public double pricePerMessage { get; set; }
public string currency { get; set; }
}
public class Status
{
public int groupId { get; set; }
public string groupName { get; set; }
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
}
public class Error
{
public int groupId { get; set; }
public string groupName { get; set; }
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public bool permanent { get; set; }
}
public class Result
{
public string messageId { get; set; }
public string to { get; set; }
public string from { get; set; }
public DateTime sentAt { get; set; }
public DateTime doneAt { get; set; }
public int smsCount { get; set; }
public string mccMnc { get; set; }
public Price price { get; set; }
public Status status { get; set; }
public Error error { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
Then do RootObject results = JsonConvert.DeserializeObject<RootObject>(JsonData);
Fair Option: Get the exact JToken you want.
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
string jsonData = "{\"results\":[{\"messageId\":\"15712480583306574\",\"to\":\"\",\"from\":\"TestServer\",\"sentAt\":\"2019-10-16T17:47:38.368+0000\",\"doneAt\":\"2019-10-16T17:47:38.370+0000\",\"smsCount\":1,\"mccMnc\":\"null\",\"price\":{\"pricePerMessage\":0.0,\"currency\":\"USD\"},\"status\":{\"groupId\":5,\"groupName\":\"REJECTED\",\"id\":8,\"name\":\"REJECTED_PREFIX_MISSING\",\"description\":\"Number prefix missing\"},\"error\":{\"groupId\":0,\"groupName\":\"OK\",\"id\":0,\"name\":\"NO_ERROR\",\"description\":\"No Error\",\"permanent\":false}}]}";
JObject jObject = JObject.Parse(jsonData);
Console.WriteLine(jObject.SelectToken("results[0].status"));
}
}

Deserializing a nested JSON string, cannot access properties

I am having issues deserializing a nested JSON array from the Genius lyric website API. I formulated the object using http://json2csharp.com. When I deserialize the object, I am unable to access the properties inside of the class, which wasn't entirely unexpected, I am just not sure how to properly design an actual solution to the problem. The JSON object conversions work fine when they are not nested.
What would be the best way to go about handling this?
Here is the conversion code:
string test = await G.SearchGeniusASync(textBox1.Text);
var data = JsonConvert.DeserializeObject<GeniusApiObject>(test);
Here is my class:
class GeniusApiObject
{
public class Meta
{
public int status { get; set; }
}
public class Stats
{
public bool hot { get; set; }
public int unreviewed_annotations { get; set; }
public int concurrents { get; set; }
public int pageviews { get; set; }
}
public class PrimaryArtist
{
public string api_path { get; set; }
public string header_image_url { get; set; }
public int id { get; set; }
public string image_url { get; set; }
public bool is_meme_verified { get; set; }
public bool is_verified { get; set; }
public string name { get; set; }
public string url { get; set; }
public int iq { get; set; }
}
public class Result
{
public int annotation_count { get; set; }
public string api_path { get; set; }
public string full_title { get; set; }
public string header_image_thumbnail_url { get; set; }
public string header_image_url { get; set; }
public int id { get; set; }
public int lyrics_owner_id { get; set; }
public string lyrics_state { get; set; }
public string path { get; set; }
public int? pyongs_count { get; set; }
public string song_art_image_thumbnail_url { get; set; }
public Stats stats { get; set; }
public string title { get; set; }
public string title_with_featured { get; set; }
public string url { get; set; }
public PrimaryArtist primary_artist { get; set; }
}
public class Hit
{
public List<object> highlights { get; set; }
public string index { get; set; }
public string type { get; set; }
public Result result { get; set; }
}
public class Response
{
public List<Hit> hits { get; set; }
}
public class RootObject
{
public Meta meta { get; set; }
public Response response { get; set; }
}
}
This is the source for the SearchGeniusASync method in case it is helpful:
public async Task<string>SearchGeniusASync(string searchParameter)
{
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", clientAccessToken);
var result = await httpClient.GetAsync(new Uri("https://api.genius.com/search?q=" + searchParameter), HttpCompletionOption.ResponseContentRead);
var data = await result.Content.ReadAsStringAsync();
return data;
}
This is the scope I am given access to:
https://i.imgur.com/9mZMvfp.png
Here's a sample JSON request in plaintext:
https://pastebin.com/iA8dQafW
GeniusApiObject is not needed in the code, but I'll leave it in just because it helps organize things (may be that something else also has a RootObject from the auto-generator).
The problem is that you are trying to deserialize to what is just an empty class, the class itself has no properties, so you can't deserialize to it. You need to deserialize to the GeniusApiObject.RootObject.
var data = JsonConvert.DeserializeObject<GeniusApiObject.RootObject>(test);
Will deserialize to the .RootObject subclass. This is verified working:
Where I'm using File.ReadAllText("test.json") to load the example API data provided.
Here is a .NET Fiddle showing it working (without the root object and only one song in the response). Thanks to #maccttura.

ServiceStack Deserialization not building an object

I'm using servicestack to deserialize a JSON I got from a web service into an object.
The process works (no exceptions) but I have no access to the classes inside the deserialized object.
my code calls this:
LoginResultModel result = new LoginResultModel
{
Avatars = new Avatars(),
Country = new Country(),
RootObject = new RootObject()
};
result = client.Post<LoginResultModel>(URL, postData);
Once I get back the result (of type LoginResultModel), I cant access anything inside of it!
Intellisense wont help - "result." dont show anything related to that class.
I'm guessing I'm doing something wrong with the hierarchy? (Weird since no exceptions are thrown). What am I doing wrong?
JSON in Deserialized form (Used json2Csharp):
public class LoginResultModel
{
/// <summary>
/// IsLogedIn method
/// </summary>
public Country Country { get; set; }
public Avatars Avatars { get; set; }
public RootObject RootObject { get; set; }
}
public class Country
{
public string Name { get; set; }
public string A2 { get; set; }
public int Code { get; set; }
public int PhonePrefix { get; set; }
}
public class Avatars
{
public string Small { get; set; }
public string Medium { get; set; }
public string Large { get; set; }
}
public class RootObject
{
public int CID { get; set; }
public string Username { get; set; }
public Country Country { get; set; }
public string URL { get; set; }
public int AffiliateID { get; set; }
public Avatars Avatars { get; set; }
public bool IsLoggedIn { get; set; }
public bool AllowCommunity { get; set; }
public string Token { get; set; }
public int TokenExpirationInMinutes { get; set; }
public bool IsKYCRequired { get; set; }
}
Your LoginResultModel class does not contain any public properties. So there is nothing to serialise, and you will then have an empty result.
What you have done is created other classes within the LoginResultModel which I believe you meant to implement as properties.
What you should really do is create the classes like this:
public class Country
{
public string Name { get; set; }
public string A2 { get; set; }
public int Code { get; set; }
public int PhonePrefix { get; set; }
}
public class Avatars
{
public string Small { get; set; }
public string Medium { get; set; }
public string Large { get; set; }
}
public class RootObject
{
public int CID { get; set; }
public string Username { get; set; }
public Country Country { get; set; }
public string URL { get; set; }
public int AffiliateID { get; set; }
public Avatars Avatars { get; set; }
public bool IsLoggedIn { get; set; }
public bool AllowCommunity { get; set; }
public string Token { get; set; }
public int TokenExpirationInMinutes { get; set; }
public bool IsKYCRequired { get; set; }
}
Where LoginResultModel has properties of the type of the other classes:
public class LoginResultModel
{
public Country Country { get; set; }
public Avatars Avatars { get; set; }
public RootObject RootObject { get; set; }
}
Then in your action method you will need to populate the LoginResultModel with an instance of those objects:
public class MyLoginService : Service
{
public LoginResultModel Post(LoginRequest request)
{
// Your login logic here
return new LoginResultModel {
Country = new Country { Name = "United States", A2 = "Something", Code = 1, PhonePrefix = 555},
Avatars = new Avatars { Small = "small.png", Medium = "medium.png", Large = "large.png" },
RootObject = new RootObject {
CID = 123,
Username = "",
...
}
};
}
}
Hope that helps.

Deserialization using Newtonsoft.Json - Assigning Default Values to Nulls Parameters

My classes are defined below
public class HotelRoomResponse
{
public string rateCode { get; set; }
public string rateDescription { get; set; }
public RoomType RoomType { get; set; }
public string supplierType { get; set; }
public int propertyId { get; set; }
public BedTypes BedTypes { get; set; }
public string smokingPreferences { get; set; }
public int rateOccupancyPerRoom { get; set; }
public int quotedOccupancy { get; set; }
public int minGuestAge { get; set; }
public RateInfos RateInfos { get; set; }
public ValueAdds ValueAdds { get; set; }
public string deepLink { get; set; }
public RoomImages RoomImages { get; set; }
}
public class ValueAdd
{
[JsonProperty(PropertyName = "#id")]
public string id { get; set; }
public string description { get; set; }
}
public class ValueAdds
{
private string valueaddsize="0";
[JsonProperty(PropertyName = "size")]
[DefaultValue("0")]
public string size
{
get
{
return !string.IsNullOrEmpty(valueaddsize)
? valueaddsize
: ((DefaultValueAttribute)
(TypeDescriptor.GetProperties(this)["size"].Attributes[
typeof(DefaultValueAttribute)])).Value.ToString();
}
set { valueaddsize = value; }
}
[JsonConverter(typeof(ObjectToArrayConverter<ValueAdd>))]
public List<ValueAdd> ValueAdd { get; set; }
}
public class RoomImage
{
//// private string useurl = "~/no-picture-available.jpg";
// [DefaultValue("~/no-picture-available.jpg")]
// public string url { get; set; }
private string useurl="~/no-picture-available.jpg";
[DefaultValue("~/no-picture-available.jpg")]
public string url
{
get
{
return !string.IsNullOrWhiteSpace(useurl)
? useurl
: ((DefaultValueAttribute)
(TypeDescriptor.GetProperties(this)["url"].Attributes[
typeof(DefaultValueAttribute)])).Value.ToString();
}
set { useurl = value; }
}
}
public class RoomImages
{
public string size { get; set; }
public RoomImage RoomImage { get; set; }
}
While in deserialization process a few parameters are missing i.e each time a few hotels will produce RoomImages and ValueAdds and a few hotel will not .
My Question is instead of sending Nulls after deserialization process, I am try to set default values but those values are not reflected after deserialization. How can I set default values to RoomImages values as "No images url" and ValueAdds as "No value adds "?
Json.Net will ignore the properties which do not exist in the json, but I think you talk about the case-2 (below). Then using NullValueHandling enum would be enough.
//Case 1 (Json doesn't contain the property)
var image = JsonConvert.DeserializeObject<RoomImage>("{}");
Console.WriteLine(image.Url); //<-- ~/no-picture-available.jpg
//Case 2 (Property is explicitly set to null)
var settings = new JsonSerializerSettings() {NullValueHandling = NullValueHandling.Ignore };
image = JsonConvert.DeserializeObject<RoomImage>("{\"Url\":null}", settings);
Console.WriteLine(image.Url); //<-- ~/no-picture-available.jpg
public class RoomImage
{
string _url = "~/no-picture-available.jpg";
public string Url { get { return _url; } set { _url = value; } }
}

Categories

Resources