I am trying to deserialize a JSON string into a large object; I'm using Angular to send my data to a controller. Data is present and correct when it arrives in the controller, but after deserialization, it's null. I've looked at some questions already, like this one here:
Deserialization of JSON.Net returns 'null'
Based on the answer to the question above, and the comment from the user who wrote the question, my code should work, but it doesn't.
Here's my code:
[HttpPost]
public OnlineOrderValidation Post(HttpRequestMessage request)
{
Task<string> result = request.Content.ReadAsStringAsync();
var json = result.Result;
OnlineOrder order = new OnlineOrder();
order = JsonConvert.DeserializeObject<OnlineOrder>(json);
// Save first
_orderFacade.UpdateCurrentOrder(CacheStore, order);
var validation = _orderFacade.ValidateOrder(order);
return validation;
}
The OnlineOrder class should accept all of the data from JsonConvert.DeserializeObject(json), but the values are either 0, null, or false.
I've tried DeserializeAnonymousType(string json, T anonObject) and JavasScriptSerializer.Deserialize(string data) as well, and they both yield an empty OnlineOrder object with null values.
I've also tried passing the object to the controller like this:
public OnlineOrderValidation Post([FromBody]string ooJson)
{
var order = JsonConvert.DeserializeObject<OnlineOrder>(ooJson);
//work with order object...
}
and like this
public OnlineOrderValidation Post([FromUri]string ooJson)
{
var order = JsonConvert.DeserializeObject<OnlineOrder>(ooJson);
//work with order object...
}
I wonder if the problem lies with 'ReadAsStringAsync()'. I noticed that it spits out each property on OnlineOrder out of order. The async part of this method must add each field/value pair as it finishes parsing the string, and since the first field in result doesn't match the first field on the OnlineOrder object, maybe JsonConvert.DeserializeObject() just stops execution.
Do I need to make a wrapper class for OnlineOrder and deserialize to that? Do I need to reorder the fields in my 'result' object before deserializing? Is there something else I'm doing wrong? Thanks all!
EDIT: Here is my JSON, as requested. The actual object is much larger than this, but you get the idea. The JSON string has valid data, but is either nulled or zeroed out after deserialization.
{"isTMIOfferEligible":false,
"tmiOfferCode":null,
"tmiUpgradeOfferCode":null,
"id":"a34mdxdivt0svmu1okucidbi",
"accountNumber":"111111111",
"transferAccountNumber":"222222222",
"serviceAddress":
{"address":"3922 N STREET AVE",
"addressLine":"3901 N STREET AVE",
"suite":"SYNCRD",
"city":"SOOFOO",
"state":"SD",
"zip":"57000",
"franchise":"111",
"market":"1 "},
"transferAddress":
{"disconnectDate":null,
"transferFromAddress":
{"address":"1234 SOME PLACE",
"addressLine":"1234 SOME PLACE",
"suite":"SYNCRD",
"city":"SOOFOO",
"state":"SD",
"zip":"57000",
"franchise":"123",
"market":"Z "
}
}
}
EDIT 2
I've updated my code to deserialize the JSON string from a custom, async method, and instantiate OnlineOrder when it's assigned:
try
{
var orderTask = ReadStringAsynchronously(request);
OnlineOrder order = orderTask.Result;
//work with order object
}
catch (Exception ex) { //...}
private async Task<OnlineOrder> ReadStringAsynchronously(HttpRequestMessage request)
{
try
{
var json = await request.Content.ReadAsStringAsync();
OnlineOrder orderAsync = JsonConvert.DeserializeObject<OnlineOrder>(json);
return orderAsync;
}
catch (Exception)
{
throw new Exception("Error occurred while reading string asynchronously.");
}
}
The order object on the task is still null.
The issue, in this case, was in the JSON string itself. There was HTML nestled within the JSON string that was not being encoded. Before posting to the server, I used encodeURIComponent(value) in a custom method:
function encodeOrderHtml(order) {
debugger;
var o = order;
//scrub HTML from product options
if (o && o.serviceDiscount) {
var sd = o.serviceDiscount;
if (sd.description !== "" && sd.description !== null) {
sd.description = encodeURIComponent(sd.description);
}
if (sd.descriptionNew !== "" && sd.descriptionNew !== null) {
sd.descriptionNew = encodeURIComponent(sd.descriptionNew);
}
if (sd.displayNameNew !== "" && sd.displayNameNew !== null) {
sd.displayNameNew = encodeURIComponent(sd.displayNameNew);
}
if (sd.name !== "" && sd.name !== null) {
sd.name = encodeURIComponent(sd.name);
}
}
return o;
}
I was able to get valid data to the server by posting the object in the body of the request.
Related
I'm writing a code to create a new object with key metadata being provided by a user input form. I'm wondering if there's a way to return the object (q) in the try block and not at the end of the method?
Here's my current code with some notes about how I want it all to look:
public NewSearchQuery GetEntry()
{
//pull all input field information and store ready for validation
string name = Convert.ToString(Companies.SelectedItem);
string location = String.Concat(Convert.ToString(Property.Text), " ", Convert.ToString(SearchLocation.Text).ToLower());
string searchtype = Convert.ToString(Search.SelectedItem);
var q = new NewSearchQuery();
//check all required input fields are filled in
if (String.IsNullOrEmpty(name) || String.IsNullOrEmpty(location) || String.IsNullOrEmpty(searchtype))
{
MessageBox.Show("Please ensure you have filled in all the required fields (*) before proceeding", "Insufficient Information");
this.ShowDialog();
}
else
{
try
{
q.GetFormData(name, location, searchtype, Paid.Checked); //replace this with a constructor for var q
q.Contract = ThisAddIn.GetContract(q.Name);
q.CreateIdNum();
q.CreateFilePath(q.Contract, q.RefNum);
q.CalculateFees();
}
catch (Exception d)
{
MessageBox.Show(Convert.ToString(d)); //return null if the try fails
}
}
return q; //relocate this to the try block
}
I want to make these changes because I suspect that returning the q value irrespective of the process working or not is causing my winform to error out it if try to exit it prematurely.
Is there a way I can get around the inevitable 'not all code paths return a value' error?
You can rewrite your method as follows:
public NewSearchQuery GetEntry()
{
//pull all input field information and store ready for validation
string name = Convert.ToString(Companies.SelectedItem);
string location = String.Concat(Convert.ToString(Property.Text), " ", Convert.ToString(SearchLocation.Text).ToLower());
string searchtype = Convert.ToString(Search.SelectedItem);
//check all required input fields are filled in
if (String.IsNullOrEmpty(name) || String.IsNullOrEmpty(location) || String.IsNullOrEmpty(searchtype))
{
MessageBox.Show("Please ensure you have filled in all the required fields (*) before proceeding", "Insufficient Information");
this.ShowDialog();
return null;
}
try
{
var q = new NewSearchQuery();
q.GetFormData(name, location, searchtype, Paid.Checked); //replace this with a constructor for var q
q.Contract = ThisAddIn.GetContract(q.Name);
q.CreateIdNum();
q.CreateFilePath(q.Contract, q.RefNum);
q.CalculateFees();
return q;
}
catch (Exception d)
{
MessageBox.Show(Convert.ToString(d)); //return null if the try fails
return null;
}
}
Now all code paths return a value. I omitted the else block, because if you leave the method inside the if block. This means, that the code following the if block is never executed when your condition is true, as it would be with the else block. The advantage of this is that you don't have so much nested bracings, which makes the code easier to understand.
Be sure to check whether the return value is not null, otherwise you might have a NullReferenceException.
Yes,
Write a return statement within every single block.
if(something)
{
return value;
}
else
{
try
{
return value;
}
catch
{
return value;
}
}
I am trying to filter out all errors except one from a list of errors I send back to the front end. I realize this operation should be an async operation as my request is giving a 500 internal server error. I am new to C# and am having a hard time figuring out how to do so.
My code that gets invoked on the route request looks like:
public async Task<ActionResult> Index(ProfileParams profileParameters)
{
// ...... //
var user = await GenerateUser(Request.RequestContext);
var userState = await _userStateFactory.CreateAsync(user);
var stateTree = new BusinessProfileStateTreeModel
{
Global = await _globalStateFactory.CreateAsync(user),
Header = await _headerStateFactory.CreateAsync(user, null),
User = userState,
Modals = _modalsStateFactory.Create(),
Page = CreatePageState(),
BusinessProfile = _businessProfileReviewsStateFactory.Create(viewModel, customerReviewModel),
Analytics = await _analyticsStateFactory.CreateAsync(user, CreateDtmData(viewModel?.Categories?.PrimaryCategoryName, profileBbbInfo?.BbbName, viewModel), userState)
};
// trying to filter out errors here from the state tree alerts
var errors = filterErrorsAsync(stateTree.BusinessProfile.Display.Alerts.AllAlerts);
var metaData =
GenerateProfileMetaData(customerReviewModel.NumFound, viewModel.ProfileUrl.ToUrlString(), viewModel);
var serverSideModel =
GenerateServerSideModel(
viewModel,
metaData,
profileBbbInfo,
stateTree.Analytics.DtmData,
user);
return await ReduxViewAsync(stateTree.ToList(), serverSideModel);
}
}
The filterErrorsAsync method looks like:
private List<BPAlert> filterErrorsAsync(List<BPAlert> allAlerts)
{
foreach (BPAlert alert in allAlerts)
{
if (alert.AlertTypeId == (int)BusinessReportCustomTextType.CustomerReviews)
{
allAlerts.Clear();
allAlerts.Add(alert);
}
}
return allAlerts;
}
Can someone tell me how to achieve this correctly?
You can't loop a list and modify it at the same time. This is probably what is causing your 500 error.
It looks like you only want filter out certain errors from a list. If you want to keep your method as a loop you can do:
private List<BPAlert> filterErrorsAsync(List<BPAlert> allAlerts)
{
List<BPAlert> temp = new List<BPAlert>(); //copy into new list
foreach (BPAlert alert in allAlerts)
{
if (alert.AlertTypeId == (int)BusinessReportCustomTextType.CustomerReviews)
{
temp.Add(alert);
}
}
return temp;
}
If you want to be a little more modern you can also just use LINQ
private List<BPAlert> filterErrorsAsync(List<BPAlert> allAlerts)
{
return allAlerts.Where(alert => alert.AlertTypeId == (int)BusinessReportCustomTextType.CustomerReviews).ToList();
}
You're attempting to modify a list while enumerating it which won't work. Since you already know which kind of error you want to filter to, you can utilize LINQ's Where method to filter out the other errors, then use Take to get the first one.
private List<BPAlert> filterErrors(List<BPAlert> allAlerts)
=> allAlerts.Where(alert => alert.AlertTypeID == (int)BusinessReportCustomTextType.CustomerReviews)
.Take(1)
.ToList();
There isn't anything asynchronous happening in this method, so no need to mark it async.
Well, the question is simple as you might seen. I need to get the error message from IHttpActionResult method, which returns BadRequest. See the example:
public async Task<IHttpActionResult> SomeMethod(string data) {
if (data==null) return BadRequest("Error messsage for you");
}
string data;
var result = await SomeMethod(data = null);
if (result is BadRequestErrorMessageResult) string error = result.ErrorMessageINeeded;
So, how do I get this one? Is there a class, which receives result object as a parameter and gets the error message from him? any ideas?
For somehow Message property is unavailable in result
if (result is BadRequestErrorMessageResult errorResult)
{
string error = errorResult.Message;
}
or, for C# versions older then 7.0:
if (result is BadRequestErrorMessageResult)
{
string error = ((BadRequestErrorMessageResult)result).Message;
}
I'm building an API to read data from stored procedure. I have to use same method to read different set of data by pass a different parameter in URL.
Below is how the paths look:
localhost:8080/api/PowerFeed/GetData/date/{date}/ClassID/{ClassID}
localhost:8080/api/PowerFeed/GetData/date/{date}/MainID/{MainID}
How do I use a single parameter to access different IDs in my method.
Method:
public IHttpActionResult GetData(DateTime date, int ClassID)
{
if(date == null)
{
Logger.Debug(
CommonConstants.Failed,
$"{nameof(PowerFeedController)}.nameof(GetData)}",
CorrelationId);
return BadRequest("Invalid request parameters. Cannot get data without
date");
}
var stopwatch = Stopwatch.StartNew();
IEnumerable<Power> records = null;
if(ClassID >= 0)
{
records = _dataAccessor.GetData(ApplicationName, date, ClassID);
Logger.Debug(stopwatch.ElapsedMilliseconds.ToString(),
$"{Operation.MeasureExecutionTimeInMilliseconds}-{nameof(PowerFeedDataAccessor)}.{nameof(_dataAccessor.GetData)}",
CorrelationId)
}
else
{
Logger.Debug(
CommonConstants.Failed,
$"{nameof(PowerFeedController)}.{nameof(GetData)}",
CorrelationId);
return BadRequest("Invalid request parameters. Cannot get data without ClassID");
}
return Ok(records);
}
In the above method, how would I pass MainID instead of ClassID, so that it can be called to get a different set of data?
Is the shape of the data that's returned (the schema) the same in both instances? If so, something more like this would be preferred:
localhost:8080/api/PowerFeed/GetData/date/{date}&classID={ClassID}&mainID=MainID
and then in the controller:
public IHttpActionResult GetData(DateTime date, int classID, int mainID)
{
if(date == null)
throw new NullReferenceException(); //etc
if(classID == 0 && mainID == 0)
throw new NullReferenceException(); //etc
// do method for each case (ClassID or MainID) here
}
If the shape / schema is different depending on the query, then you should have 2 separate controllers.
You could try a PUT like below:
[HttpPut]
[Route("startthread")]
public async Task<WebApiResponse> StartThread(StartThreadRequest request)
{
// logic
// return the response;
}
Using RestSharp I'm building an API to perform CRUD operations given a datatype/object.
My CrudAbstract class is generic and has the following:
public virtual async Task<keyType> Post(dto item)
{
try
{
var request = await _client.GetRequestAsync(_path);
request.Method = RestSharp.Method.POST;
request.AddJsonBody(item);
var result = await _client.ExecuteRequestAsync<keyType>(request);
return result;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
throw new Exception("Was not able to process crud post operation.");
}
My WebClient class has the following:
Entities = new CrudAbstract<DtoEntity, int>("/entities", this); // in constructor
// So the keyType from definition above is int (Int32)
The post method in this class is
public async Task<T> ExecuteRequestAsync<T>(IRestRequest request)
{
try
{
var response = await GetClient().ExecuteTaskAsync<T>(request);
// Exception occurs here. The above statement is unable to finish.
var data = response.Data;
return data;
}
catch (Exception e)
{
// Log exception
}
throw new Exception("Was not able to process restclient execute async method.");
}
My Api EntitiesController has the following:
public int Post(DtoEntity value)
{
using (var db = // some database...)
{
try
{
// Upsert object into database here
//... value.Id no longer null at this point
/*
The problem occurs here. I only want to return the ID of the object
(value.Id). I do not want to return the whole object (value)
*/
return value.Id;
}
catch (Exception e)
{
// Log exception
}
}
throw new Exception("Was not able to process entities post method.");
}
The exception I get is:
Unable to cast object of type 'System.Int64' to type
'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
This is basically saying it is unable to cast the object int (which I have returned in the post with value.Id) to a DtoEntity object (which is the actual object on which CRUD operations were performed).
What am I doing wrong?
I have placed typeof and .getType() onto each keyType, T, and value.Id, and all of them are Int32. Is it in the RestSharp library that the problem occurs? At what stage is there a casting of int to a DtoEntity in the line:
var response = await GetClient().ExecuteTaskAsync<T>(request);
Note: when I change the return type of the post method in my controller to DtoEntity and change the value.Id to just value it works. The response is received, and the response.Data is the DtoEntity object.
I've seen similar questions here but not found a solution yet.
I believe you've spotted a bug in RestSharp. RestSharp internally uses a JSON parser called SimpleJson (borrowed from the Facebook .NET SDK). It appears that this parser is correctly deserializing the response body to a number (since JSON is untyped it uses Int64 to be safe), but the RestSharp's JsonDeserializer class attempts to cast this result to an IDictionary in the first line of this method:
private object FindRoot(string content)
{
var data = (IDictionary<string, object>)SimpleJson.DeserializeObject(content);
if (RootElement.HasValue() && data.ContainsKey(RootElement))
{
return data[RootElement];
}
return data;
}
I think your options are:
File an issue with the RestSharp team.
Get the raw string body and cast it to an int yourself (may be the path of least resistance).
Try a different library.