I have a C# view class such as this:
public class DataObject
{
public int Number { get; set; }
public dynamic Data { get; set; } // <-----
}
being used in an MVC method like this
[HttpPost]
public ActionResult SaveData(DataObject request) {}
The problem is that I want to recieve multiple types of objects in the Data property of the DataObject class.
That is, I want both these to work as valid input json objects.
Type 1
{
Number: 1,
Data: {
Text: "a text"
}
}
Type 2
{
Number: 2,
Data: {
Value: 1,
Options: { 1, 2, 3, 4 }
}
}
Is there a way of doing this with either dynamic objects or some other type of json library magic (just making the property dynamic did nothing)?
All i want to do is store this data in a SQL column nvarchar field and return at a later time (through Entity Framework).
An alternate solution would be to create a view model for each type of input but as there will be 100's of variants to it creating all these views and the corresponding input methods would be cumbersome to maintain.
Adding more details as per comment request: The method is called through Angular.
pub.Save = function (jsonData) {
return $http(
{
method: "POST",
url: baseURL + "/Save",
data: { request: jsonData}, // tried this for string
// data: jsonData, // original way
timeout: 30000
}
)
.then(function (result) {
return result.data;
});
}
At the server side, DTO class must match with the same property name which the payload is carrying.
public class DataObject
{
public string test { get; set; } // <-----
}
So, your save method remains the same:
[HttpPost]
public ActionResult SaveData(DataObject request) {}
The payload json is in the object request.test but its seralized.
Deseralize it using Json Library.
How is it handling multiple different types of variables?
Deseralize it to a dynamic type as:
dynamic obj = JsonConvert.DeserializeObject(request.test, typeof(object));
//Properties within the obj are checked at run time.
if(obj.Text != null) {
//Do your thing
}
if(obj.Value != null) {
//Do your thing
}
if(obj.Options != null) {
//Do your thing
}
By converting the data to a JSON string on the client side I was able to send it to the string property and thus being able to use the same typed view for all objects.
I ended up doing this when saving the object (I'm using angular on the front end), converting the Json object to a string.
entry.Data = angular.toJson(entryData.Data, false);
And then when getting the json string back from MVC I did this to get it back to a real javascript object.
entry.Data = angular.fromJson(entry.Data);
MVC would not accept the JSON object into the text property without making it into a json string first.
Using the above method I am storing data like this in my database:
"{\"Value\":123,\"Currency\":\"EUR\"}"
How should I show text from a enum column instead of its value in a jQuery DataTable, without losing its value as I'll be using it later for editing purposes?
Given a Class:
public class Person
{
[Key]
public int PersonUID { get; set; }
public string FirstName { get; set; }
public EstadoPersona Status { get; set; }
}
And a Enum:
public enum EstadoPersona
{
Active, Inactive
}
I'm populating a jQuery DataTable using the following action from Controller:
public ActionResult List()
{
var data = db.People.Select(
a => new
{
a.FirstName,
a.Status,
a.PersonUID
}).OrderByDescending(a => a.PersonUID).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
And this is my DataTable set up:
$("#tblPersons").DataTable({
processing: true,
dom: 'Brtip',
ajax: {
type: "POST",
url: "/People/List",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: function (data) {
return data = JSON.stringify(data);
},
dataSrc: "",
error: function (jqXHR, textStatus, errorThrown) {
debugger; alert("ajax error: " + textStatus);
}
},
columns: [
{data: "FirstName" },
{ data: "Status"},
{ data: "PersonUID" }
],
rowId: 'PersonUID',
},
order: [],
});
Use Json.Net http://www.newtonsoft.com/json and decorate following attribute for the Enum type column
public class Person
{
[Key]
public int PersonUID { get; set; }
public string FirstName { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public EstadoPersona Status { get; set; }
}
to post the values as {"Status":"Active"} or {"Status":"InActive"} instead of {"Status":0} or {"Status":1}
Note: I wrote this whole answer saying you couldn't then found out a way to do it in the question I linked to show that you couldn't (d'oh), using the JSON.NET extension. See the Edit if you are willing to use that extension (it's pretty great).
Original Answer
See this stack overflow question (quoted the important answer below)
JavaScriptSerializer [what is used when you convert the data to JSON] serializes enums to their numeric values and not their string representation. You would need to use custom serialization to serialize the enum as its name instead of numeric value.
Unfortunately, when you convert the enum to JSON, it has been converted to an int and the enum status has been lost.
This means that you can't simply send the string (and that wouldn't be good enough for you anyway, since you want to have both the string value and the numeric value).
What I'd recommend doing is changing the object that is sent to DataTables to be a KeyValuePair<string,int>. Now, this isn't a perfect solution, since you'll have to convert from the enum to the KeyValuePair both before you send and after you receive the data, but it's probably the simplest way to keep the string name and numeric value attached to each other when they're used in the JavaScript.
Basically, you'll have to modify your creation of the objects sent to DataTables so that instead of just adding a.status you can instead in that object creation, create the key-value pair.
While this isn't really what you wanted, it is an unfortunate fact that you can't serialize an enum into JSON and keep the name, only the value. You'll have to either convert the enum into another type that can hold two values (KeyValuePair, an array, a Dictionary, whatever) during the serialization process or just stop using an enum.
Edit: Upon looking further into the linked question, it looks like if you use the JSON.NET extension, you actually can serialize enums. If you're willing to use that extension, that could be a very nice solution to your problem. I've used it before, and it's pretty useful and not difficult to use.
[JsonConverter(typeof(StringEnumConverter))]
Is the syntax you'd use if you use JSON.NET. You can get some more detail in the second answer on the question I linked.
Edit 2: After seeing Vijai's answer (which shows a nice example of how you would actually add the above attribute to your class), it appears that this will only send the string name of the enum, not both the name and the value. You may still have to send an object other than an enum, as in my original answer, if you want both the string and int values.
I have a Web API service call that updates a user's preferences. Unfortunately when I call this POST method from a jQuery ajax call, the request parameter object's properties are always null (or default values), rather than what is passed in. If I call the same exact method using a REST client (I use Postman), it works beautifully. I cannot figure out what I'm doing wrong with this but am hoping someone has seen this before. It's fairly straightforward...
Here's my request object:
public class PreferenceRequest
{
[Required]
public int UserId;
public bool usePopups;
public bool useTheme;
public int recentCount;
public string[] detailsSections;
}
Here's my controller method in the UserController class:
public HttpResponseMessage Post([FromBody]PreferenceRequest request)
{
if (request.systemsUserId > 0)
{
TheRepository.UpdateUserPreferences(request.UserId, request.usePopups, request.useTheme,
request.recentCount, request.detailsSections);
return Request.CreateResponse(HttpStatusCode.OK, "Preferences Updated");
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.NotAcceptable, "You must provide User ID");
}
}
Here's my ajax call:
var request = {
UserId: userId,
usePopups: usePopups,
useTheme: useTheme,
recentCount: recentCount,
detailsSections: details
};
$.ajax({
type: "POST",
data: request,
url: "http://localhost:1111/service/User",
success: function (data) {
return callback(data);
},
error: function (error, statusText) {
return callback(error);
}
});
I've tried setting the dataType & contentType to several different things ('json', 'application/json', etc) but the properties of the request object are always defaulted or null. So, for example, if I pass in this object:
var request = {
UserId: 58576,
usePopups: false,
useTheme: true,
recentCount: 10,
detailsSections: ['addresses', 'aliases', 'arrests', 'events', 'classifications', 'custody', 'identifiers', 'phone', 'remarks', 'watches']
}
I can see a fully populated request object with the valid values as listed above. But in the Web API controller, the request is there, but the properties are as follows:
UserId: 0,
usePopups: false,
useTheme: false,
recentCount: 0,
detailsSections: null
FYI - I'm not doing ANY ASP.Net MVC or ASP.NET pages with this project, just using the Web API as a service and making all calls using jQuery $.ajax.
Any idea what I'm doing wrong here? Thanks!
UPDATE: I just want to note that I have many methods in this same Web API project in other controllers that do this exact same thing, and I am calling the exact same way, and they work flawlessly! I have spent the morning comparing the various calls, and there doesn't appear to be any difference in the method or the headers, and yet it just doesn't work on this particular method.
I've also tried switching to a Put method, but I get the exact same results - the request object comes in, but is not populated with the correct values. What's so frustrating is that I have about 20 controller classes in this project, and the Posts work in all of those...
This seems to be a common issue in regards to Asp.Net WebAPI.
Generally the cause of null objects is the deserialization of the json object into the C# object. Unfortunately, it is very difficult to debug - and hence find where your issue is.
I prefer just to send the full json as an object, and then deserialize manually. At least this way you get real errors instead of nulls.
If you change your method signature to accept an object, then use JsonConvert:
public HttpResponseMessage Post(Object model)
{
var jsonString = model.ToString();
PreferenceRequest result = JsonConvert.DeserializeObject<PreferenceRequest>(jsonString);
}
So there are 3 possible issues I'm aware of where the value does not bind:
no public parameterless constructor
properties are not public settable
there's a binding error, which results in a ModelState.Valid == false - typical issues are: non compatible value types (json object to string, non-guid, etc.)
So I'm considering if API calls should have a filter applied that would return an error if the binding results in an error!
Maybe it will help, I was having the same problem.
Everything was working well, and suddently, every properties was defaulted.
After some quick test, I found that it was the [Serializable] that was causing the problem :
public IHttpActionResult Post(MyComplexClass myTaskObject)
{
//MyTaskObject is not null, but every member are (the constructor get called).
}
and here was a snippet of my class :
[Serializable] <-- have to remove that [if it was added for any reason..]
public class MyComplexClass()
{
public MyComplexClass ()
{
..initiate my variables..
}
public string blabla {get;set;}
public int intTest {get;set;
}
I guess problem is that your controller is expecting content type of [FromBody],try changing your controller to
public HttpResponseMessage Post(PreferenceRequest request)
and ajax to
$.ajax({
type: "POST",
data: JSON.stringify(request);,
dataType: 'json',
contentType: "application/json",
url: "http://localhost:1111/service/User",
By the way creating model in javascript may not be best practice.
Using this technique posted by #blorkfish worked great:
public HttpResponseMessage Post(Object model)
{
var jsonString = model.ToString();
PreferenceRequest result = JsonConvert.DeserializeObject<PreferenceRequest>(jsonString);
}
I had a parameter object, which had a couple of native types and a couple more objects as properties. The objects had constructors marked internal and the JsonConvert.DeserializedObject call on the jsonString gave the error:
Unable to find a constructor to use for type ChildObjectB. A class
should either have a default constructor, one constructor with
arguments or a constructor marked with the JsonConstructor attribute.
I changed the constructors to public and it is now populating the parameter object when I replace the Object model parameter with the real object. Thanks!
I know, this is a bit late, but I just ran into the same issue. #blorkfish's answer worked for me as well, but led me to a different solution. One of the parts of my complex object lacked a parameter-less constructor. After adding this constructor, both Put and Post requests began working as expected.
I have also facing this issue and after many hours from debbug and research can notice the issue was not caused by Content-Type or Type $Ajax attributes, the issue was caused by NULL values on my JSON object, is a very rude issue since the POST neither makes but once fix the NULL values the POST was fine and natively cast to my respuestaComparacion class here the code:
JSON
respuestaComparacion: {
anioRegistro: NULL, --> cannot cast to BOOL
claveElector: NULL, --> cannot cast to BOOL
apellidoPaterno: true,
numeroEmisionCredencial: false,
nombre: true,
curp: NULL, --> cannot cast to BOOL
apellidoMaterno: true,
ocr: true
}
Controller
[Route("Similitud")]
[HttpPost]
[AllowAnonymous]
public IHttpActionResult SimilitudResult([FromBody] RespuestaComparacion req)
{
var nombre = req.nombre;
}
Class
public class RespuestaComparacion
{
public bool anioRegistro { get; set; }
public bool claveElector { get; set; }
public bool apellidoPaterno { get; set; }
public bool numeroEmisionCredencial { get; set; }
public bool nombre { get; set; }
public bool curp { get; set; }
public bool apellidoMaterno { get; set; }
public bool ocr { get; set; }
}
Hope this help.
I came across the same issue. The fix needed was to ensure that all serialize-able properties for your JSON to parameter class have get; set; methods explicitly defined. Don't rely on C# auto property syntax! Hope this gets fixed in later versions of asp.net.
A bit late to the party, but I had this same issue and the fix was declaring the contentType in your ajax call:
var settings = {
HelpText: $('#help-text').val(),
BranchId: $("#branch-select").val(),
Department: $('input[name=departmentRadios]:checked').val()
};
$.ajax({
url: 'http://localhost:25131/api/test/updatesettings',
type: 'POST',
data: JSON.stringify(settings),
contentType: "application/json;charset=utf-8",
success: function (data) {
alert('Success');
},
error: function (x, y, z) {
alert(x + '\n' + y + '\n' + z);
}
});
And your API controller can be set up like this:
[System.Web.Http.HttpPost]
public IHttpActionResult UpdateSettings([FromBody()] UserSettings settings)
{
//do stuff with your UserSettings object now
return Ok("Successfully updated settings");
}
In my case problem was solved when i added
get{}set{}
to parameters class definition:
public class PreferenceRequest
{
public int UserId;
public bool usePopups {get; set;}
public bool useTheme {get; set;}
public int recentCount {get; set;}
public string[] detailsSections {get;set;}
}
enstead of:
public class PreferenceRequest
{
[Required]
public int UserId;
public bool usePopups;
public bool useTheme;
public int recentCount;
public string[] detailsSections;
}
As this issue was troubling me for almost an entire working day yesterday, I want to add something that might assist others in the same situation.
I used Xamarin Studio to create my angular and web api project. During debugging the object would come through null sometimes and as a populated object other times. It is only when I started to debug my project in Visual Studio where my object was populated on every post request. This seem to be a problem when debugging in Xamarin Studio.
Please do try debugging in Visual Studio if you are running into this null parameter problem with another IDE.
Today, I've the same problem as yours. When I send POST request from ajax the controller receive empty object with Null and default property values.
The method is:
[HttpPost]
public async Task<IActionResult> SaveDrawing([FromBody]DrawingModel drawing)
{
try
{
await Task.Factory.StartNew(() =>
{
//Logic
});
return Ok();
}
catch(Exception e)
{
return BadRequest();
}
}
My Content-Type was correct and everything else was correct too. After trying many things I found that sending the object like this:
$.ajax({
url: '/DrawingBoard/SaveDrawing',
type: 'POST',
contentType: 'application/json',
data: dataToPost
}).done((response) => { });
Won't work, but sending it like this instead worked:
$.ajax({
url: '/DrawingBoard/SaveDrawing',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(dataToPost)
}).done((response) => { });
Yes, the missing JSON.stringify() caused the whole issue, and no need to put = or anything else as prefix or suffix to JSON.stringify.
After digging a little bit. I found that the format of the request payload completely different between the two requests
This is the request payload with JSON.stringify
And this is the request payload without JSON.stringify
And don't forget, when things get magical and you use Google Chrome to test your web application. Do Empty cache and hard reload from time to time.
I ran into the same issue, the solution for me was to make certain the types of my class attributes matched the json atributes, I mean
Json: "attribute": "true"
Should be treated as string and not as boolean, looks like if you have an issue like this all the attributes underneath the faulty attribute will default to null
I ran into the same problem today as well. After trying all of these, debugging the API from Azure and debugging the Xamarin Android app, it turns out it was a reference update issue. Remember to make sure that your API has the Newtonsoft.JSON NUGET package updated (if you are using that).
My issue was not solved by any of the other answers, so this solution is worth consideration:
I had the following DTO and controller method:
public class ProjectDetailedOverviewDto
{
public int PropertyPlanId { get; set; }
public int ProjectId { get; set; }
public string DetailedOverview { get; set; }
}
public JsonNetResult SaveDetailedOverview(ProjectDetailedOverviewDto detailedOverview) { ... }
Because my DTO had a property with the same name as the parameter (detailedOverview), the deserialiser got confused and was trying to populate the parameter with the string rather than the entire complex object.
The solution was to change the name of the controller method parameter to 'overview' so that the deserialiser knew I wasn't trying to access the property.
I face this problem this fix it to me
use attribute [JsonProperty("property name as in json request")]
in your model by nuget package newton
if you serializeobject call PostAsync only
like that
var json = JsonConvert.SerializeObject(yourobject);
var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
var response = await client.PostAsync ("apiURL", stringContent);
if not work remove [from body] from your api
HttpGet
public object Get([FromBody]object requestModel)
{
var jsonstring = JsonConvert.SerializeObject(requestModel);
RequestModel model = JsonConvert.DeserializeObject<RequestModel>(jsonstring);
}
public class CompanyRequestFilterData
{
public string companyName { get; set; }
public string addressPostTown { get; set; }
public string businessType { get; set; }
public string addressPostCode{ get; set; }
public bool companyNameEndWith{ get; set; }
public bool companyNameStartWith{ get; set; }
public string companyNumber{ get; set; }
public string companyStatus{ get; set; }
public string companyType{ get; set; }
public string countryOfOrigin{ get; set; }
public DateTime? endDate{ get; set; }
public DateTime? startDate { get; set; }
}
webapi controller
[HttpGet("[action]"),HttpPost("[action]")]
public async Task<IEnumerable<CompanyStatusVm>> GetCompanyRequestedData(CompanyRequestFilterData filter)
{}
jsvascript/typescript
export async function GetCompanyRequesteddata(config, payload, callback, errorcallback) {
await axios({
method: 'post',
url: hostV1 + 'companydata/GetCompanyRequestedData',
data: JSON.stringify(payload),
headers: {
'secret-key': 'mysecretkey',
'Content-Type': 'application/json'
}
})
.then(res => {
if (callback !== null) {
callback(res)
}
}).catch(err => {
if (errorcallback !== null) {
errorcallback(err);
}
})
}
this is the working one c# core 3.1
my case it was bool datatype while i have changed string to bool [companyNameEndWith] and [companyNameStartWith] it did work. so please check the datatypes.
The following screen shot shows the data I'm sending (shown with firebug in firefox).
The code below then shows the method the ajax method calls. The Date and Id properties are correctly populated when hitting the server side method call but my array (of type CustomerRequests) has no values inside it, however the number of CustomerRequests in the post is correct.
Any ideas?
Thanks
My Controller method
public ActionResult Show(Customers request)
{
..
// Number of request.CustomerRequests is correct
// Although request.CustomerRequests[0].Name == null ?? which is wrong
Customers class below:
[DataContract]
public class Customers
{
[DataMember]
public CustomerRequests[] CustomerRequests{ get; set; }
[DataMember]
public DateTime Date { get; set; } // I can see this value
[DataMember]
public int Id{ get; set; } // I can see this value
}
[DataContract]
public class CustomerRequests
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Expression { get; set; }
}
Javascript
$('textarea').each(function () {
var theName = 'The Name';
var theExpression = 'The Expression';
var obj = {
'Name': theName,
'Expression': theExpression
};
expressionArray.push(obj);
}); // close each
// val is the posted data
var val = {
'Id': '1',
'Date': '2013-10-10',
'CustomerRequests': $.makeArray(expressionArray)
};
I've tried although it doesn't work.
JSON.stringify({ Customers: val })
After all, we just have to add a content-type to your call and to remove the name from data.
It's something like
$.ajax({
url: '/controller/action',
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(viewModel)
});
Please note that:
hard coding the URL is a bad practice. You should use #Url.Content("") or something like that
JSON.stringify may not work with older browsers. You may need to add this lib: http://www.json.org/js.html
Although this is not exactly an answer, because I have never used the built-in serializers, I can advise you to use the Json.NET library. I've been using it now for a long time, and I like it very much.
As for your question, I would try using List<T> instead of array, and I would probably try to instantiate the List in the constructor of the model. But these are just guesses, to be honest.
I am having trouble model binding a JSON array to a C# list in MVC 3.
I have an object called a DockState. It looks like this:
[Serializable]
public class DockState
{
public bool Closed { get; set; }
public bool Collapsed { get; set; }
public string DockZoneID { get; set; }
public int ExpandedHeight { get; set; }
public Unit Height { get; set; }
public int Index { get; set; }
public Unit Left { get; set; }
public bool Pinned { get; set; }
public bool Resizable { get; set; }
public string Tag { get; set; }
public string Text { get; set; }
public string Title { get; set; }
public Unit Top { get; set; }
public string UniqueName { get; set; }
public Unit Width { get; set; }
}
My action method accepts a list of dock states, like this:
public JsonResult SaveDockStates(List<DockState> dockStates)
On the client I am building an array of JSON objects that contain all the same properties as the C# DockState object. I am then assigning this array to a JSON object of its own with the property of the argument in the action method, like this:
function get_allDockStates()
{
var allRadDocks = []; // declare array.
var allRadControls = $telerik.radControls; // use telerik API to obtain all controls.
// loop through all telerik controls.
for (var i = 0; i < allRadControls.length; i++)
{
var element = allRadControls[i];
// Check if control is a rad dock element.
if (Telerik.Web.UI.RadDock && element instanceof Telerik.Web.UI.RadDock)
{
// Build a JSON object containing the same properties as the C# DockState
// object. Leaving out a couple that should just be null anyway. Add new
// JSON object to array.
Array.add(allRadDocks,
{
UniqueName: element._uniqueName,
DockZoneID: element._dockZoneID,
Width: element._width,
Height: element._height,
ExpandedHeight: element._expandedHeight,
Top: element._top,
Left: element._left,
Resizable: element._resizable,
Closed: element._closed,
Collapsed: element._collapsed,
Pinned: element._pinned,
Title: element._title,
Index: element._index
});
}
}
// Return the array.
return allRadDocks;
}
// This function is fired by the rad dock controls when they are moved.
function positionChanged(sender, e)
{
// obtain the array of dock states.
var dockStates = get_allDockStates();
// Make ajax call to MVC action method to save dock states.
jQuery.ajax({
data: { dockStates: dockStates },
type: 'POST',
dataType: 'json',
url: '/AjaxServices/DashboardService/SaveDockStates'
});
}
Unfortunately something rather odd happens when the AJAX call is made. It hits the action method and my List<DockState> does have items in it.
http://www.codetunnel.com/content/images/dockStateListInstantiated.jpg
However, the items in the list are all default. Meaning all their values are the default values, not the ones submitted in the request.
http://www.codetunnel.com/content/images/dockStatesDefaultValues.jpg
I'm not sure why the list contains the correct number of items, but all items appear to be nothing more than instantiated. Their properties were not set to the values in the JSON array. The request definitely contains the right data, as shown here:
http://www.codetunnel.com/content/images/dockStatesFormData.jpg
Am I doing something wrong? Is the form data formatted incorrectly? Is there a problem with the default model binder?
Update (testing Darin Dimitrov's answer)
I discovered that JSON was being overridden by an unused old script. I have removed and now JSON.stringify is working fine. Here is the request payload.
http://www.codetunnel.com/content/images/dockStatesJsonStringify.jpg
Much better. However now when I am debugging my list has zero items in it. This did not seem to fix the problem, though I appreciate the effort :)
Try sending a JSON request:
jQuery.ajax({
// TODO: Never hardcode urls like this => always use URL helpers
// when you want to generate an url in an ASP.NET MVC application
url: '/AjaxServices/DashboardService/SaveDockStates',
type: 'POST',
data: JSON.stringify({ dockStates: dockStates }),
contentType: 'application/json; charset=utf-8',
success: function(result) {
// TODO: process the results
}
});
The JSON.stringify method is natively built into modern browsers. If you want to support legacy browsers you need to include the json2.js script to your page.