I have a JSON array, which looks like this:
{[
{
"personnelId": 201,
"occupationIds": [
4,
5
]
},
{
"personnelId": 202,
"occupationIds": [
5
]
}
]}
I'm receiving this string through a JObject in my ASP.NET Web API controller:
var jPersonnelAndOccupationIds = json["personnelAndOccupationIds"];
I've had success deserialiizing various data structures through JObject but this keeps giving a RuntimeBinderException, what data structure would be best suited for that JSON string, I've tried a lot of thing, this IMO is the most suitable List<PersonnelOccupationsVm> where PersonnelOccupationsVm looks like this:
public class PersonnelOccupationsVm
{
public long personnelId { get; set; }
public List<long> occupationIds { get; set; }
}
which I desrialize like this:
var personnelIdsAndOccupationsKvp = jPersonnelAndOccupationIds.ToObject(new List<PersonnelOccupationsVm>());
and I've tried
System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<List<PersonnelOccupationsVm>>(jPersonnelAndOccupationIds);
neither works. any ideas?
EDIT
As requested in the comments, I'm making my call like this (angular):
$http({
method: 'POST',
url: '/api/EnterprisePersonnelApi/SubmitOcupationalPersonnel/',
data: JSON.stringify({ enterpriseId: enterpriseId, positionId: positionId,personnelAndOccupationIds: personnelAndOccupationIds }),
}).success(function (data, status, headers, config) {
deferred.resolve(data);
}).error(function (data, status, headers, config) {
deferred.reject(status);
});
and my web api action:
[System.Web.Http.HttpPost]
public CrudOperationResults SubmitOcupationalPersonnel(JObject jsonData){
dynamic json = jsonData;
var jPersonnelAndOccupationIds = json["personnelAndOccupationIds"];
var personnelIdsAndOccupations = jPersonnelAndOccupationIds.ToObject(new List<PersonnelOccupationsVm>());
.....
}
this given a RunTimeBinderexception, however when I call .ToString() while retrieving values from the JObject instance:
var jPersonnelAndOccupationIds = json["personnelAndOccupationIds"].ToString();
and (per shunty's suggestion)
var jPersonnelAndOccupationIds = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PersonnelOccupationsVm>>(jPersonnelAndOccupationIds);
this works fine, the leading and trailing curly braces were added while retieving my parameter values from JObject, calling .ToString() and deserializing it per above did the trick.
I'm not a particularly proficient Json user but, using the Newtonsoft stuff I'd try something like this (cut and pasted from LINQPad):
void Main()
{
string json = "[{ \"personnelId\": 201, \"occupationIds\": [4,5]}, {\"personnelId\": 202,\"occupationIds\": [5]}]";
var js = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PersonnelOccupationsVm>>(json);
js.Dump();
}
public class PersonnelOccupationsVm
{
public int PersonnelId { get; set; }
public List<int> OccupationIds { get; set; }
public PersonnelOccupationsVm()
{
OccupationIds = new List<int>();
}
}
Note that I had to trim the leading and trailing { } characters from the source Json.
Related
It seems things have become more complex going from ASP.NET MVC to .NET Core because I can no longer easily send List of objects to controller using Ajax. Am I doing something wrong?
In my controller, I have this method:
[HttpPost("EditMultipleResults")]
[Consumes("application/x-www-form-urlencoded")]
public bool EditMultipleResults([FromForm] List<Result>, [FromForm] string comment)
{
// do something...
return true;
}
Result is defined here
public class Result
{
[Key]
public long taskcd { get; set; }
public long Runno { get; set; }
public string Workorder {get; set;}
}
In my JS Ajax I have:
var results = [
{taskcd: 123,
Runno: 187776876,
Workorder: 'VA1234567'
},
{taskcd: 642,
Runno: 187776877,
Workorder: 'VA1234569'
},
{taskcd: 766,
Runno: 187776876,
Workorder: 'VA1234564'
}
];
var posteddata = {
results: results,
comment: 'test comment'
};
// call the controller
$.ajax({
type: 'POST',
data: posteddata,
traditional: true,
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
url: 'api/ResultsEditor/EditMultipleResults',
success: function () {
deferred.resolve();
},
error: deferred.reject
});
return deferred.promise();
My problem is that results list and reason are null when in controller. How can I pass a list of objects to controller in .NET Core 5?
Another question: is there a way to see the data that's being passed to controller in dev tools?
you can't use [FromForm] (as well as [FromBody]) multiple times, your action input parameter should be the same as it is in ajax, so you will have to create a class
public class PostedData
{
public List<ResultItem> Results { get; set; }
public string Comment { get; set; }
};
and action
public bool EditMultipleResults(PostedData data)
{
// do something...
return true;
}
and you don't need to use [Consumes("application/x-www-form-urlencoded")] ,[FromForm] and contentType: 'application/x-www-form-urlencoded; charset=utf-8' since all of them are already by default.
Because you are creating form data as a json object manually, instead of posting an html form, you need to encode that data as x-www-form-urlencoded manually also.
This post has some great suggestions. Make sure to read not just the accepted answer.
I have an object with predefined data structure:
public class A
{
public string Id {get;set;}
public bool? Enabled {get;set;}
public int? Age {get;set;}
}
and JSON is supposed to be
{ "Id": "123", "Enabled": true, "Age": 23 }
I want to handle JSON error in positive way, and whenever server returns unexpected values for defined data-types I want it to be ignore and default value is set (null).
Right now when JSON is partially invalid I'm getting JSON reader exception:
{ "Id": "123", "Enabled": "NotABoolValue", "Age": 23 }
And I don't get any object at all.
What I want is to get an object:
new A() { Id = "123", Enabled = null, Age = 23 }
and parsing warning if possible.
Is it possible to accomplish with JSON.NET?
To be able to handle deserialization errors, use the following code:
var a = JsonConvert.DeserializeObject<A>("-- JSON STRING --", new JsonSerializerSettings
{
Error = HandleDeserializationError
});
where HandleDeserializationError is the following method:
public void HandleDeserializationError(object sender, ErrorEventArgs errorArgs)
{
var currentError = errorArgs.ErrorContext.Error.Message;
errorArgs.ErrorContext.Handled = true;
}
The HandleDeserializationError will be called as many times as there are errors in the json string. The properties that are causing the error will not be initialized.
Same thing as Ilija's solution, but a oneliner for the lazy/on a rush (credit goes to him)
var settings = new JsonSerializerSettings { Error = (se, ev) => { ev.ErrorContext.Handled = true; } };
JsonConvert.DeserializeObject<YourType>(yourJsonStringVariable, settings);
Props to Jam for making it even shorter =)
There is another way. for example, if you are using a nuget package which uses newton json and does deseralization and seralization for you. You may have this problem if the package is not handling errors. then you cant use the solution above. you need to handle in object level. here becomes OnErrorAttribute useful. So below code will catch any error for any property, you can even modify within the OnError function and assign default values
public class PersonError
{
private List<string> _roles;
public string Name { get; set; }
public int Age { get; set; }
public List<string> Roles
{
get
{
if (_roles == null)
{
throw new Exception("Roles not loaded!");
}
return _roles;
}
set { _roles = value; }
}
public string Title { get; set; }
[OnError]
internal void OnError(StreamingContext context, ErrorContext errorContext)
{
errorContext.Handled = true;
}
}
see https://www.newtonsoft.com/json/help/html/SerializationErrorHandling.htm
Here is my jQuery which looks at an HTML table and gets an id from the tr and an input field value and puts them in an object to be json stringified and posted to an MVC controller. I am using jQuery 1.8.2
var rowdata = [];
$('table').find('tr').each(function () {
myjson = [];
item = {}
item["id"] = $(this).attr('id');
item["reason"] = $(this).find('input').val();
myjson.push(item);
rowdata.push(myjson);
});
jsonstring = JSON.stringify(rowdata);
$.ajax({
url: '#Url.Action("AbsentReason", "Attendance")',
data: jsonstring,
type: 'POST',
traditional: true,
contentType: 'json',
success: function (data) {
$('#message').html("Reason was updated");
}
});
This is the resulting JSON which checks valid.
[[{}],[{"id":"6","reason":""}],[{"id":"7","reason":""}],[{"id":"15","reason":""}],[{"id":"23","reason":""}],[{"id":"29","reason":""}],[{"id":"30","reason":""}],[{"id":"31","reason":""}],[{"id":"35","reason":""}],[{"id":"40","reason":""}],[{"id":"41","reason":""}],[{"id":"42","reason":""}],[{"id":"48","reason":""}],[{"id":"49","reason":""}],[{"id":"50","reason":""}],[{"id":"51","reason":""}],[{"id":"52","reason":""}],[{"id":"53","reason":""}],[{"id":"54","reason":""}],[{"id":"55","reason":""}],[{"id":"56","reason":""}],[{"id":"57","reason":""}],[{"id":"58","reason":""}],[{"id":"59","reason":""}],[{"id":"60","reason":""}],[{"id":"61","reason":""}],[{"id":"62","reason":""}],[{"id":"63","reason":""}],[{"id":"74","reason":""}],[{"id":"75","reason":""}],[{"id":"80","reason":""}],[{"id":"81","reason":""}],[{"id":"87","reason":""}],[{"id":"88","reason":""}],[{"id":"90","reason":""}],[{"id":"91","reason":""}],[{"id":"105","reason":""}],[{"id":"106","reason":""}],[{"id":"107","reason":""}],[{"id":"108","reason":""}],[{"id":"110","reason":""}],[{"id":"111","reason":""}],[{"id":"119","reason":""}]]:
This is the start of my controller.
[HttpPost]
public ActionResult AbsentReason(string jsonstring)
{
return View("Index");
}
The jsonstring parameter is always null. Can anyone see what is wrong?
UPDATE
This is my new controller based on the comments to use a model and allow MVC to do the work for me.
[HttpPost]
public ActionResult AbsentReason(IEnumerable<VMAttendance> jsonstring)
{
return View("Index");
}
and my viewmodel
public class VMAttendance
{
public int PersonID
{
get;
set;
}
public string Reason
{
get;
set;
}
}
The parameter is still null. I also update my jQuery in an attempt to send the correct json.
var data = $('table').find('tr').map(function () {
var id = $(this).attr('id');
var reason = $(this).find('input').val();
var rowdata = { "PersonID": id, "Reason": reason };
return rowdata;
}).get();
$.ajax({
url: '#Url.Action("AbsentReason", "Attendance")',
data: data,
type: 'POST',
traditional: true,
contentType: 'json',
success: function (data) {
$('#message').html("Reason was updated");
}
});
I tried to send some test json to the controller but the parameter is still null.
var data = '{"PersonID":"6","Reason":""},{"PersonID":"7","Reason":""}'
assuming you have an MVC model as follow
public class MyModel
{
public int ID {get;set;}
public string Reason {get;set;}
}
if you modify your action method signature to
public ActionResult AbsentReason(IEnumerable<MyModel> json)
when you post back your json object, the json serializer will deserialize your json into an IEnumerable of MyModel.
I got this working by changing the contentType from 'json' to 'application/json; charset=utf-8' and removing the first empty object in my json which was created from an extra tr in the table for the header. The json looked like
[{},{"PersonID":"6","Reason":""},{"PersonID":"7","Reason":""}]
when it should look like this
[{"PersonID":"6","Reason":""},{"PersonID":"7","Reason":""}]
My controller looks like this and works nice.
[HttpPost]
public ActionResult AbsentReason(IEnumerable<VMAttendance> jsonstring)
{
return View("Index");
}
Thanks Duy for your assistance!
change the contentType in your ajax object to string.
i'm curious as to why you would want to treat your json as string and not as object. Normally I would have a C# viewmodel and let the serializer map the json object to that C# view model in my controller
I have got the problem with json binding to the view model. Here is my code:
part of my ViewModels (AddressViewModel has more properties):
public class AddressViewModel
{
[Display(Name = "Address_Town", ResourceType = typeof(Resources.PartyDetails))]
public string Town { get; set; }
[Display(Name = "Address_Country", ResourceType = typeof(Resources.PartyDetails))]
public Country Country { get; set; }
}
public class Country : EntityBase<string>
{
public string Name { get; set; }
protected override void Validate()
{
if (string.IsNullOrEmpty(Name))
{
base.AddBrokenRule(new BusinessRule("CountryName", "Required"));
}
}
}
Javascript:
$(document).on("click", "#addAddress", function () {
var jsonData = {
"Town": $('#txt-Town').val(),
"District": $('#txt-District').val(),
"Street": $('#txt-Street').val(),
"PostCode": $('#txt-PostCode').val(),
"FlatNumber": $('#txt-FlatNumber').val(),
"PremiseName": $('#txt-PremiseName').val(),
"PremiseNumber": $('#txt-Premisenumber').val(),
"Country": {
"Name": $('#txt-Country').val(),
}
};
var addressData = JSON.stringify(jsonData);
$.ajax({
url: '/Customer/SaveAddress',
type: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: addressData,
success: function (result) {
$("#addIndividualAddressDialog").data("kendoWindow").close();
},
error: function (result) {
alert("Failed");
}
});
});
Header of controller:
[HttpPost]
public ActionResult SaveAddress(AddressViewModel addressViewModel)
This is what I see with firebug:
And this is what I see in VS:
As you can see Plain properties are binded correct but my nested object (Country) is null. I read a lot of different articles and I still don't know what I'm doing wrong. Help me please!
You can keep your existing ActionMethod untouched without the need of json serializing:
In the client side create an object from your json:
JSON.parse(jsonData)
and send that in the $.ajax data property.
Or, instead of creating json, create an object:
var dataObject = new Object();
dataObject.Town = $('#txt-Town').val();
dataObject.District = $('#txt-District').val();
...
And again, send that object in the $.ajax data property.
The problem is from your action method parameter:
[HttpPost]
public ActionResult SaveAddress(AddressViewModel addressViewModel)
As you use JSON.stringify(), you send a string to your controller, not an object! So, you need to do some works to achive your goal:
1) Change your action method parametter:
[HttpPost]
public ActionResult SaveAddress(string addressViewModel)
2) Deserialize that string to an object - that is AddressViewModel:
IList<AddressViewModel> modelObj = new
JavaScriptSerializer().Deserialize<IList<AddressViewModel>>(addressViewModel);
So, your final action method should be like the following:
[HttpPost]
public ActionResult SaveAddress(string addressViewModel)
{
IList<AddressViewModel> modelObj = new
JavaScriptSerializer().Deserialize<IList<AddressViewModel>>(addressViewModel);
// do what you want with your model object ...
}
Actually the best option is just to remove the
var addressData = JSON.stringify(jsonData);
line and send jsonData itself. ASP.NET MVC will auto-bind it if it is an actual object and not just a string.
Occam's Razor
Sorry to answer for old thread.here you could work with JsonResult instead of ActionResult
this is your signature
[HttpPost]
public ActionResult SaveAddress(AddressViewModel addressViewModel)
it should look like
[HttpPost]
public JsonResult SaveAddress(AddressViewModel addressViewModel)
{
return Json(status);
}
the advantage would be if you use JsonResult instead of ActionResult that u do not need to Deserialize
here is the link http://codeforcoffee.org/asp-net-mvc-intro-to-mvc-using-binding-json-objects-to-models/ from this link you can get the idea.
OR you can use JsonConvert.DeserializeObject<>();
following is the code to deserialize the JSON.stringify() result
IList<AddressViewModel> modelObj = JsonConvert.DeserializeObject<IList<AddressViewModel>>(addressViewModel);
instead of
JavaScriptSerializer().Deserialize<IList<AddressViewModel>>(addressViewModel);
You must reference to Country object inside jsonData variable. The JSON POST binding will work correctly.
Old code:
var jsonData = {
"Town": $('#txt-Town').val(),
"District": $('#txt-District').val(),
"Street": $('#txt-Street').val(),
"PostCode": $('#txt-PostCode').val(),
"FlatNumber": $('#txt-FlatNumber').val(),
"PremiseName": $('#txt-PremiseName').val(),
"PremiseNumber": $('#txt-Premisenumber').val(),
"Country": {
"Name": $('#txt-Country').val(),
}
New code:
var jsonData = {
"Town": $('#txt-Town').val(),
"District": $('#txt-District').val(),
"Street": $('#txt-Street').val(),
"PostCode": $('#txt-PostCode').val(),
"FlatNumber": $('#txt-FlatNumber').val(),
"PremiseName": $('#txt-PremiseName').val(),
"PremiseNumber": $('#txt-Premisenumber').val(),
"Country.Name": $('#txt-Country').val(),
}
I want to receive json object from view to controller using $.ajax method but i dont know why the receive object in controller shows null data.
here is my code.
$("#Save").click(function (e) {
$.ajax({ url: "Home/Save",
type: "POST",
datatype:'json',
data: ({movies:movies})
});
});
where movies is javascript array which contains
({
Name:"DDLJ",Stars:"SRK",Director:"Yashraj",Year:"2012"
}, {Name:"K3G",Stars:"SRK",Director:"Karan",Year:"2010"}
)
and my controller code is:
public string Save (List<MovieDB> movies)
{
return "";
}
where MovieDB is my model class which have the properties
public class MoviesDB
{
//public int MoviesID { get; set; }
public string Name { get; set; }
public string Stars { get; set; }
public string Director { get; set; }
public DateTime Year { get; set; }
}
Please suggest me any better way to receive json data from view to controller.
Actully I try to send javascript array object from view to controller action method in which my array code is
<script type="text/javascript">
var cnt = 1;
var cnt2 = 1;
var i;
var movies = [];
movies.push({ Name: $("#txtMovieName").val(), Stars: $("#txtStarCasts").val(), Director: $("#txtDirector").val(), Year: $("#txtYear").val(), Index: cnt++ });
$("#modelTable").empty();
$("#modelTemplate").tmpl(movies).appendTo($("#modelTable"));
$("#txtMovieName").val("");
$("#txtStarCasts").val("");
$("#txtDirector").val("");
$("#txtYear").val("");
$("#txtMovieName").focus();
e.preventDefault();
});
$("#Save").click(function (e) {
//var jm = $.toJSON(movies);
$.ajax({ url: "Home/Save",
type: "POST",
data: ({movies:movies})
// contentType: 'application/json; charset=utf-8'
});
});
</script>
Now I want to send that movies javascript array object to Save() Action method of controller using any way like json or other please suggest me something...
This is not a correct json. You can check this on JSONLint
Here is the correct json:
[
{
"Name": "DDLJ",
"Stars": "SRK",
"Director": "Yashraj",
"Year": "2012"
},
{
"Name": "K3G",
"Stars": "SRK",
"Director": "Karan",
"Year": "2010"
}
]
If there are still errors, let me know.
Maybe this is a better answer, if you want to get your data into your controller after a post.
//For going to your view.
public ActionResult Create()
{
MoviesDB model = new MoviesDB();
return View("Create", model);
}
[HttpPost]
public ActionResult Create(MoviesDB model)
{
if (ModelState.IsValid)
{
//Here you can use your model param
//with the filled in values of your view.
return RedirectToAction("Index");
//when everything works fine, go to this view.
}
else
{
return RedirectToAction("Index");
//when there is a problem, go to this view.
}
return View("Index");
}
You don't need jQuery to use your data from your view.
List<xyztype> lstid = new JavaScriptSerializer().Deserialize<List<xyztype>>(IDlist);
is working for me
here xyztype is my collection class type and IDlist is my json objectstring from view to controller action method
by using javascriptserializer().deserializer<> we can convert json string to our custom or system datatype object
for example :
List<string> lstid = new JavaScriptSerializer().Deserialize<List<string>>(IDlist);