Mapping from JSON body to model issue because of system naming - c#

In the request body I there is a property named systemDate. This property is always set to 0 in my model and I thought it was because of the variable type (long , double, etc) but after I changed the name from systemDate to someDate in the request body and from SystemDate to SomeDate in the model class the value is passed from the request body to the model instance just as it is supposed to be.
Why is this happening and is there a way to keep the request json naming and make it passing its value to the model?
{
"category":"some_category",
"level":5,
"source":"some_source",
"location":"some_location",
"date":2793455394017,
"message":"some_message",
"id":3295830,
"systemDate":1533114073596991534
}
Here is how my model class looks like:
public class MyModel
{
public MyModel()
{
}
public string Category { get; set; }
public int Level { get; set; }
public string Source { get; set; }
public string Location { get; set; }
public double Date { get; set; }
public string Message { get; set; }
public long Id { get; set; }
public double SystemDate { get; set; }
}
And the Controller method:
[HttpPost(EndpointUrlConstants.MY_ENDPOINT)]
public async Task<IActionResult> DoSomething([FromBody] MyModel myModel)
{
// Some Code
return this.Ok();
}

For Asp.Net Core, we could configure the Json Serialize Settings by AddJsonOptions in Startup.
And the root cause for this issue is related with NamingStrategy = new SnakeCaseNamingStrategy().

I'm not sure if I understand your problem, but you can control the serialization using attributes, i.e. property names in the json string don't have to match the property names in the model.
public class MyModel
{
public MyModel()
{
}
[JsonProperty("category")]
public string Category { get; set; }
[JsonProperty("level")]
public int Level { get; set; }
[JsonProperty("source")]
public string Source { get; set; }
[JsonProperty("location")]
public string Location { get; set; }
[JsonProperty("date")]
public double Date { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("systemDate")]
public double SomeDate { get; set; }
}
Test code, using Newtonsoft.Json nuget package:
string json = #"{
""category"":""some_category"",
""level"":5,
""source"":""some_source"",
""location"":""some_location"",
""date"":2793455394017,
""message"":""some_message"",
""id"":3295830,
""systemDate"":1533114073596991534
}";
MyModel model = JsonConvert.DeserializeObject<MyModel>(json);
Object is deserialized correctly. As you can see, SomeDate property in the model is mapped to match systemDate property in the json string.

Related

Getting Errors when trying to Deserialize JSON object from API call in C#. (currency exchange)

My goal is to build an API that can load currency data from https://openexchangerates.org/ api into our SQLite database. I am able to get the JSON object formatted like this:
When i run the code below, it returns an errors. I am not sure how to resolve this. Just need to convert this JSON into a C# class without error.
Here's the custom C# object to mapped to JSON obj:
public class ExchangeRate
{
public string Disclaimer { get; set; }
public string License { get; set; }
public string Timestamp { get; set; }
public string Base { get; set; }
public string Rates { get; set; }
}
Here is the api call where its returning the error:
public static async Task<List> GetLatest(string url)
{
var client = new HttpClient();
string results = await client.GetStringAsync(url);
List<ExchangeRate> ratesList = JsonConvert.DeserializeObject<List<ExchangeRate>>(results);
return ratesList;
}
The example JSON is not a list, it's a single object, this is specified in the exception message
...because the type requires a JSON array
, otherwise it would have [ ] around it indicating an array (can be deserialized to list). Also, your model is flawed as Rates is not a string, but an object, and Timestamp is not a string but a long for the datetime as ticks. Change your model like so:
public class ExchangeRate
{
//decorate your properties since the json string uses lowercase
[JsonProperty("disclaimer")]
public string Disclaimer { get; set; }
[JsonProperty("license")]
public string License { get; set; }
[JsonProperty("timestamp")]
public long Timestamp { get; set; }
[JsonProperty("base")]
public string Base { get; set; }
[JsonProperty("rates")]
public Rates Rates { get; set; }
}
public class Rates
{
//create the properties for the Rates class
}
OR make the rates property a Dictionary<string, decimal>, NOTE: this could fail if any key is duplicated.
public class ExchangeRate
{
//decorate your properties since the json string uses lowercase
[JsonProperty("disclaimer")]
public string Disclaimer { get; set; }
[JsonProperty("license")]
public string License { get; set; }
[JsonProperty("timestamp")]
public long Timestamp { get; set; }
[JsonProperty("base")]
public string Base { get; set; }
[JsonProperty("rates")]
public Dictionary<string, decimal> Rates { get; set; }
}
Change your code to this:
ExchangeRate rate = JsonConvert.DeserializeObject<ExchangeRate>(results);
Your C# class model does not match the incoming JSON data structure. Rates is an array of items, but you're treating it like a string in your C# model. Timestamp is a number but you're treating it like a string in your C# model.
public class ExchangeRate
{
public string Disclaimer { get; set; }
public string License { get; set; }
public int Timestamp { get; set; }
public string Base { get; set; }
public Dictionary<string, double> Rates { get; set; }
}

Asp.Net Core Web API 2.2 Controller not returning complete JSON

I have a Web API Controller in my Asp.Net Core Web API 2.2 project.
Messageboard model:
public class MessageBoard
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Message> Messages { get; set; }
}
Message model:
public class Message
{
public long Id { get; set; }
public string Text { get; set; }
public string User { get; set; }
public DateTime PostedDate { get; set; }
public long MessageBoardId { get; set; }
[ForeignKey("MessageBoardId")]
public MessageBoard MessageBoard { get; set; }
}
This is one of my Web API Controller actions, shortened for brevity:
[Route("api/[controller]")]
[ApiController]
public class MessageBoardsController : ControllerBase
{
// GET: api/MessageBoards
[HttpGet]
public async Task<ActionResult<IEnumerable<MessageBoard>>> GetMessageBoards()
{
return await _context.MessageBoards
.Include(i => i.Messages)
.ToListAsync();
}
}
Whenever I issue a GET request to MessageBoards, only part of the correct JSON is returned. Here is the returned JSON from accessing https://localhost:44384/api/MessageBoards/ on Postman:
[{"id":1,"name":"Test Board 2","description":"A 2nd Message board for
testing purposes.","messages":[{"id":1,"text":"Posting my first
message!","user":"Jesse","postedDate":"2019-01-01T00:00:00","messageBoardId":1
The JSON is cut-off (hence why it's an ugly block and not beautified by Postman), presumably due to the MessageBoard property on the Message model since it is the first missing JSON item.
How can I make the action correctly return the list of MessageBoards and child Messages?
I see you are using Eager Loading in your query. So add the following configuration in your Startup class to ignore cycles that it finds in the object graph and to generate JSON response properly.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
For more details:
Related data and serialization in EF Core
The selected answer was correct in my case as well, my JSON response was getting truncated by a reference loop in my JSON response, and setting ReferenceLoopHandling.Ignore did indeed solve my issue. However, this is not the best solution in my opinion, as this maintains the circular references in your model. A better solution would use the [JsonIgnore] attribute within the model.
The issue in your model is here:
public class MessageBoard
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Message> Messages { get; set; }
}
public class Message
{
public long Id { get; set; }
public string Text { get; set; }
public string User { get; set; }
public DateTime PostedDate { get; set; }
public long MessageBoardId { get; set; }
[ForeignKey("MessageBoardId")]
public MessageBoard MessageBoard { get; set; } //This is the cause of your circular referece!!!
}
As you can see, your MessageBoard navigation property is where this response is truncated. Specifically, it will cause each Message in the json response to contain all of the MessageBoard information for each Message entry in the response. Newtonsoft does not like this. The solution is to simply [JsonIngore] the navigation properties that cause this circular reference. In your code this would be:
public class MessageBoard
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Message> Messages { get; set; }
}
public class Message
{
public long Id { get; set; }
public string Text { get; set; }
public string User { get; set; }
public DateTime PostedDate { get; set; }
public long MessageBoardId { get; set; }
[JsonIgnore]
[ForeignKey("MessageBoardId")]
public MessageBoard MessageBoard { get; set; } //fixed!!!
}
Try to create and return a DTO or a new struct/class that will not have cyclic info (MessageBoard has Message that has MessageBoard etc...)

nested json deserialization - need another set of eyes

I have been trying to get this json to deserialize for two days now using RestSharp. I have gone through the RestSharp github site, looked at countless examples, and spent much time here on Stack Overflow to try and find the answer to no avail. My code had previously worked perfectly but the vendor changed their API version and I was forced to do an update to keep using the application for my legal practice. My json is as follows(client info has been removed and replaced with generic info):
{
"data":[
{
"id":1035117666,
"client":
{
"id":905422394,
"name":"client1"
},
"display_number":"11-00012",
"description":"General",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2011-12-14",
"close_date":null,
"billing_method":"hourly"
},
{
"id":1035117768,
"client":
{
"id":905422506,
"name":"client2"
},
"display_number":"12-00037",
"description":"HOA",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2012-08-07",
"close_date":null,
"billing_method":"hourly"
}
],
"meta":
{
"paging":
{
"next":"https://app.goclio.com/api/v4/matters.json?fields=id%2C+client%7Bid%2C+name%7D%2C+display_number%2C+description%2C+practice_area%7Bid%2C+name%7D%2C+status%2C+open_date%2C+close_date%2C+billing_method&limit=2&page_token=BAh7BjoLb2Zmc2V0aQc%3D--b1ea3eba20c8acefbcdfc7868debd1e0ee630c64&status=Open"
},
"records":91
}
}
I built the following schema within my c# code:
public class MatterList
{
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
public class Meta
{
public Paging paging { get; set; }
public int records { get; set; }
}
public class Paging
{
public string previous { get; set; }
public string next { get; set; }
}
[DeserializeAs(Name = "data")]
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
public class PracticeArea
{
public int id { get; set; }
public string name { get; set; }
}
public class Client
{
public int id { get; set; }
public string name { get; set; }
}
When I run the RestSharp deserialize method I am sending the result to an object of type MatterList using the following line of code
MatterList matterList = jsonHandler.Deserialize<MatterList>(response);
I have so far attempted to deserialize without the Meta or Paging POCO classes with the accompanying change to the MatterList class (taking out the Meta property).
I have tried with and without the [DeserializeAs(Name="data")] directive.
I have tried to set the RootElement of the json response prior to deserialization.
I have tried to shorthand the deserialization by combining it with the Execute request code
IRestResponse<MatterList> matterList = client.Execute<MatterList>(request);
I have created a container class called MatterContainer which I placed between MatterList and Matter classes in the schema:
public class MatterList
{
public List<MatterContainer> matters { get; set; }
}
public class MatterContainer
{
public Matter matter { get; set; }
}
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
I know I am getting the json response back from the server correctly so my request is proper and MatterList is not null after deserialization. The problem is that I cannot get the deserialization to actually populate the List matters within the MatterList class.
I have been looking at this off and on for two days and cannot get past this hurdle. If anyone sees what I did wrong I would greatly appreciate the insight, I am at a point where I cannot progress further with my application.
Thanks!
I think your [DeserializeAs(Name = "data")] attribute is in the wrong place. Try putting it in the root class instead:
public class MatterList
{
[DeserializeAs(Name = "data")]
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
alternatively, try renameing that property to data

Store Object with Dynamic property in Cosmos DB

I have a message processor where I would like to take a lump of json with a wrapper of known schema, but with a property that is a dynamic object like the following:
public class NotificationDetails
{
[JsonProperty(PropertyName = "id")]
public string NotificationID { get; set; }
public DateTime? DateCreated { get; set; }
public DateTime? DateSent { get; set; }
public string TemplateUrl { get; set; }
public dynamic Model { get; set; }
}
as you can see, the last property is of dynamic. the notifications will all have different Model schemas, so I would like it to just be stored as a nested object.
That said, when I attempt to Create the object via
client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, collectionId), item)
I get the following error message:
The best overloaded method match for 'MyClass.CreateNotification(NotificationDetails))' has some invalid arguments
I thought I could throw anything into these docs. what am I doing wrong? should I use something other than dynamic for this Model property?
UPDATE I figured out it was something about how I was calling the Wait() method on the task returned from the DocumentClient. Once I reverted to the async await strategy it started working correctly.
According to your description. I have tested your code and it worked as below. You can refer to what I did:
public static void CreateCosmosDocument()
{
DocumentClient client = new DocumentClient(new Uri("https://xxxxx/"), "C2y6yDjf5/R+ob0N8A7Cgv30VRDJxxxxM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", new ConnectionPolicy { EnableEndpointDiscovery = false });
TestEntity testEntity = new TestEntity { x = 11, y = 11, name = "wakaka", dynam = "hello dynam" };
var createdItem = client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri("ToDoList", "Items"), new NotificationDetails { DateCreated=DateTime.Now, DateSent=DateTime.Now, TemplateUrl="www.baidu.com", Model= testEntity });
}
Class of NotificationDetails:
public class NotificationDetails
{
[JsonProperty(PropertyName = "id")]
public string NotificationID { get; set; }
public DateTime? DateCreated { get; set; }
public DateTime? DateSent { get; set; }
public string TemplateUrl { get; set; }
public dynamic Model { get; set; }
}
Class of TestEntity which act as a nested object:
class TestEntity
{
public ObjectId _id { get; set; }
public string name { get; set; }
public double x { get; set; }
public double y { get; set; }
public double z { get; set; }
public dynamic dynam { get; set; }
}
Screenshot of result:
If the error still occured, you'd better share your more detailed code with us for further research.

How to bind multilevel json data to a repeater in asp.net or converting json data to data table

I want to bind the Json data to the repeater I know only one process that is converting the Json data to data table and then binding data but here I am receiving multilevel json data i do't know how to convert them to data table
input json data:
{"apiAvailableBuses":
[{"droppingPoints":null,"availableSeats":40,"partialCancellationAllowed":false,"arrivalTime":"01:00 AM","cancellationPolicy":"[{\"cutoffTime\":\"1\",\"refundInPercentage\":\"10\"},{\"cutoffTime\":\"2\",\"refundInPercentage\":\"50\"},{\"cutoffTime\":\"4\",\"refundInPercentage\":\"90\"}]","boardingPoints":[{"time":"07:40PM","location":"K.P.H.B,Beside R.S Brothers","id":"2238"}],"operatorName":"Apple I Bus","departureTime":"8:00 PM","mTicketAllowed":false,"idProofRequired":false,"serviceId":"6686","fare":"1000","busType":"Hi-Tech A/c","routeScheduleId":"6686","commPCT":9.0,"operatorId":203,"inventoryType":0},
{
"droppingPoints":null,"availableSeats":41,"partialCancellationAllowed":false,"arrivalTime":"06:00 AM","cancellationPolicy":"[{\"cutoffTime\":\"1\",\"refundInPercentage\":\"10\"},{\"cutoffTime\":\"2\",\"refundInPercentage\":\"50\"},{\"cutoffTime\":\"4\",\"refundInPercentage\":\"90\"}]","boardingPoints":[{"time":"08:00PM","location":"Punjagutta,","id":"2241"}],"operatorName":"Royalcoach Travels","departureTime":"8:00 PM","mTicketAllowed":false,"idProofRequired":false,"serviceId":"6736","fare":"800","busType":"VOLVO","routeScheduleId":"6736","commPCT":9.0,"operatorId":243,"inventoryType":0}
I am trying to convert it to data table by
public void getavailablebuses()
{
string url = string.Format(HttpContext.Current.Server.MapPath("files/getavailablebuses.json"));
using (WebClient client = new WebClient())
{
string json = client.DownloadString(url);
var result = JsonConvert.DeserializeObject<RootObject>(json);
string mm = JObject.Parse(json).SelectToken("apiAvailableBuses").ToString();
// var boardingpoint = JObject.Parse(mm).SelectToken("boardingPoints").ToString();
var Availablebuses = JObject.Parse(json).SelectToken("apiAvailableBuses").ToString();
DataTable dt = (DataTable)JsonConvert.DeserializeObject(Availablebuses, (typeof(DataTable)));
}
public class apiresult
{
public string message { get; set; }
public string success { get; set; }
}
public class RootObject
{
public apiresult apiStatus;
public List<apiAvailableBuses> apiAvailableBuses{ get; set; }
// public string apiAvailableBuses { get; set; }
}
public class apiAvailableBuses
{
public string serviceId { get; set; }
public string fare { get; set; }
public string busType { get; set; }
public string departureTime { get; set; }
public string operatorName { get; set; }
public string cancellationPolicy { get; set; }
public List<boardingpoints> boardingpoints { get; set; }
public string droppingPoints { get; set; }
public string inventoryType { get; set; }
public string routeScheduleId { get; set; }
public int availableSeats { get; set; }
public string arrivalTime { get; set; }
public Boolean idProofRequired { get; set; }
public Boolean partialCancellationAllowed { get; set; }
public int operatorId { get; set; }
public double commPCT { get; set; }
public string mTicketAllowed { get; set; }
}
public class boardingpoints
{
public string location { get; set; }
public string id { get; set; }
public string time { get; set; }
}
public class cancellationPolicy
{
public string cutoffTime { get; set; }
public string refundInPercentage { get; set; }
}
Here in the data table I am unable to get the boarding points, dropping points and cancellation policy
if I load cancellation policy as list or JObject I am getting error
so here I am loading cancellation policy as string.
but I am unable to load boarding points and dropping points.
Please help with this I am scratching my head from two days. Thanks in advance
"I know only one method to bind data to a repeater i.e data table." So this is a perfect opportunity to learn other ways, wouldn't you say?
Why don't you work with the result of JsonConvert.DeserializeObject<RootObject>(json);? This is a RootObject that has a property called apiAvailableBuses which seems to be exactly what you need to bind to your repeater, no?
By the way, a bit of code review:
apiresult and apiAvailableBuses violate Microsoft's rules WRT class names: those should be in PascalCase. Same for the properties of apiresult, e.g. message and success. Same for the properties of apiAvailableBuses.
RootObject has a public field: apiStatus. That probably needs to be a a property with a getter/setter.
Moreover, apiAvailableBuses is plural, which is incorrect, since the data therein is of only one bus. Same for boardingpoints: the class contains data for a single point, not multiple.
Be consistent: if you use string, then also use bool and not Boolean.

Categories

Resources