I have a JavaScript object in my app that has an array object, which on the server-side, is a collection of objects with two properties (Name, Value).
I'm sure I'm missing something simple here since I've been staring at it too long, but when the following code makes it to the C# web service, the CustomProperties object as an array of 4 objects. However, each Name and Value property is null.
myObject.CustomProperties = [];
myObject.CustomProperties.push({ Name: "FirstName", Value: $scope.userInfo.FirstName });
myObject.CustomProperties.push({ Name: "LastName", Value: $scope.userInfo.LastName });
myObject.CustomProperties.push({ Name: "Email", Value: $scope.userInfo.Email });
myObject.CustomProperties.push({ Name: "PortalId", Value: portalId });
I've tried this too...
myObject.CustomProperties = [];
myObject.CustomProperties.push({ "Name": "FirstName", "Value": $scope.userInfo.FirstName });
myObject.CustomProperties.push({ "Name": "LastName", "Value": $scope.userInfo.LastName });
myObject.CustomProperties.push({ "Name": "Email", "Value": $scope.userInfo.Email });
myObject.CustomProperties.push({ "Name": "PortalId", "Value": portalId });
All of the above variables have values in the debugger, but the array must not be getting loaded right, because the web service is only showing null values.
This is the code that calls the web service. I've removed what I think are unnecessary bits though.
factory.callPostService("ActionName", myObject)
.success(function (data) {
// nothing in here happens
})
.error(function (data, status) {
// this always occurs
$scope.HasErrors = true;
console.log("Unknown error occurred calling ActionName");
console.log(data);
});
The server-side code that I use looks pretty much identical to the rest of my classes and properties.
Here's the property as it is for the myObject in my example.
public List<CustomPropertyInfo> CustomProperties { get; set; }
And here's the CustomPropertyInfo class.
[Serializable]
public class CustomPropertyInfo : ICustomPropertyInfo
{
public string Name { get; set; }
public string Value { get; set; }
}
The issue is caused because the Json Serializer does not know how to deserialize the collection correctly. The easiest way to resolve this is to mark the CustomPropertyInfo class with Json attributes to tell Json.Net how to handle this object. This eliminates any confusion caused by serializing/deserializing a generic List.
[Serializable]
[JsonObject(MemberSerialization.OptIn)]
public class CustomPropertyInfo : ICustomPropertyInfo
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("Value")]
public string Value { get; set; }
}
Related
I have this Object
{
"Data1": 1,
"Data2": "some string",
"Data3": [
{
"ID": 0,
"Name": "some name",
"SomeArray": []
},
{
"ID": 0,
"Name": "another name",
"SomeArray": [
"DataA": 0,
"DataB": "some data again"
]
}],
"Data4": "some string again"
}
The model which it will receive in the controller is this:
public class Data
{
public int Data1 { get; set; }
public string Data2 { get; set; }
public List<AnotherClass> Data3 { get; set; }
public string Data4 { get; set; }
}
public class AnotherClass
{
public int ID { get; set; }
public string Name { get; set; }
public List<DataList> SomeArray { get; set; }
}
public class DataList
{
public int DataA { get; set; }
public string DataB { get; set; }
}
The data that is to be set in Data3 is coming from a table where new inputs are retrieved from a modal popup and populated in the table. SomeArray data is coming from a dropdown which uses Select2 tool to set multiple choices in a dropdown.
The idea is that once the user clicks "Save", it will get all of the data, including those in the table and form this JSON object. From this object, I want to convert it into a FormData. However when it iterates into Data3, it doesn't convert it into an array of objects even if I stringify it. I've also tried this one Convert JS Object to form data but was unsuccessful.
Here's what I did so far:
var details = GetDetails(); // This contains `Data1`, `Data2` and `Data4`
var data3 = GetData3FromTable(); // This contains the list of `Data3`
var result = new FormData();
for (var key in details) {
result.append(key, details[key]);
}
result.append("Data3", JSON.stringify(data3));
result.append("UploadFile", $("#upload")[0].files[0]);
// do ajax put or post after this line
Is there any jquery or javascript plugin that I can use to convert this object into FormData? Or is there any other way to convert it into FormData?
You can send deeply nested data in HTML forms, but I have noticed that different web servers may interpret the more complex structures differently, so you may need to check your framework/app's middleware to ensure proper decoding. You may find it easier to read values into Javascript and send the data from there.
Your Data3 object could be represented using something like this:
<input type="text" name="data[Data3][0][id]" value="0">
<input type="text" name="data[Data3][0][Name]" value="some name">
<input type="text" name="data[Data3][0][SomeArray][]" value="">
You need a "parent" field (data in this example) which houses the child elements.
Note that you have to be careful of any [] -- you may need to specify the array index manually as above, otherwise the "grouping" of values won't be respected.
Also beware of names ending with []: those fields will still send values, even if it's an empty string, so you may need to filter those out.
I found this link object-to-form-data where it converts any object, including nested arrays and upload files into FormData. However, there's just one change that needs to be done:
From the link, replace objectToFormData(obj[property], fd, property); with objectToFormData(obj[property], fd, formKey);. This way, it sets the original property as the key. The output of the FormData when it is passed to the controller will be this:
Data1: 1
Data2: some string
Data3[0][ID]: 0
Data3[0][Name]: some name
Data3[0][SomeArray]: []
Data3[1][ID]: 0
Data3[1][Name]: another name
Data3[1][SomeArray][0][DataA]: 0
Data3[1][SomeArray][0][DataB]: some data again
Data4: some string again
I hope this helps others!
I'm trying to pass a object which has multiple objects inside it as below
[Object, Object, Object, Object]
0
:
Object
ProductID
:
"50"
__proto__
:
Object
1
:
Object
BrandID
:
24
__proto__
:
Object
2
:
Object
BrandID
:
26
__proto__
:
Object
3
:
Object
BrandID
:
20
__proto__
:
Object
One of these objects has different key value pair than the others. How can I get capture this data from a Web Api controller. How should I modify my Model in the Web Api project.
It seems to me that the array you are trying to send to Web API contains different objects with different schemas. This approach is certainly error prone, and will not allow you to use ModelBinding properly.
Why don't you change the format of your object to something like this?
$scope.myObject = {
ProductID: 50,
BrandIDs: [24, 26, 20]
};
Using this kind of object you will be able to bind it to a strongly typed model in Web API.
public class MyModel {
public int ProductID { get; set; }
public List<int> BrandIDs { get; set; }
}
public IHttpActionResult Post(MyModel model) {
var productId = model.ProductID;
foreach(var brandId in model.BrandIDs) {
DoSomething(brandId);
}
return Ok();
}
You just need to create a class model that corresponds to you JSON and Web Api will automatically bind it. It seems that what you are passing is an array, so you can do something like that:
public void Execute(Model[] input)
{
}
....
public class Model
{
public int? ProductId {get;set;}
public int? BrandId {get;set;}
}
Or if you want one object with an array inside you can pass a class like that
public class ProductsContainer
{
public Product[] Products {get;set;}
}
I am working on an AngularJS App, and one of the methods in the service js post data to a web api with a following object structure in C#
public class InvitationModel
{
public string Name { get; set; }
public string Email { get; set; }
public EventModel[] EventList { get; set; }
}
public class EventModel
{
public string EventName { get; set; }
public int TotalAdults { get; set; }
public int TotalChildren { get; set; }
public bool IsAccepted { get; set; }
}
Problem is that when I post this data to a WEBAPI method, my parent level properties serializes correctly except the one that holds the collection. It gets set to null always.
The web API method that recieves the request is :
[AllowAnonymous]
[Route("RSVP")]
[HttpPost]
public bool Submit(InvitationModel invitationModel)
{
return true;
}
So, Name and Email serialize correctly, but EventList is NULL
I did check on the javascript side, and my js object holds both the array and other primitive properties. Issue I guess is at the .NET WebAPI side.
Request payload that gets posted is something like this :
{ "Name":"John Doe",
"EventList":{
"0":{ "TotalAdults":"1",
"TotalChildren":"2",
"EventName":"Event 1 Name"
},
"1":{ "TotalChildren":"2",
"TotalAdults":"2",
"EventName":"Event 2 Name"
},
"2":{ "TotalAdults":"1",
"TotalChildren":"1",
"EventName":"Event 3 Name"
}
}
}
The EventList in your JSON is an object with properties "0", "1", etc.
I guess it should be a JSON array, i.e.
{
"Name":"John Doe",
"EventList": [
{"TotalAdults":"1","TotalChildren":"2","EventName":"Event 1 Name"},
{"TotalChildren":"2","TotalAdults":"2","EventName":"Event 2 Name"},
...
], ...
to be correctly read into your C# eventlist property.
I'm using a third party library and cant really change the way it posts data to my MVC 5 controller.
I cant figure out how to setup my model to receive the data.
The json is a follows...
{
"expiration":"2015-06-14T21:02:52.969Z",
"conditions":[
{"acl":"private"},
{"bucket":"anyoldbucket"},
{"Content-Type":"application/pdf"},
{"success_action_status":"200"},
{"key":"somekey"}
]
}
I tried setting up my model like this...
public class AwsSignatureRequestViewModel
{
public DateTime expiration { get; set; }
public Dictionary<string, string> conditions { get; set; }
}
The expiration date is correctly filled out, and I get the right number of conditions but the keys to the dictionary are numbers (indexes) and the values are null
Any suggestions?
If your model is strict, you will need to make objects for every sub objects.
But if you have a dynamic model, you can read the raw string from your request and parse it with Json.net.
public ActionResult Test(string model)
{
Request.InputStream.Seek(0, SeekOrigin.Begin);
string jsonData = new StreamReader(Request.InputStream).ReadToEnd();
var dynamicObject = JObject.Parse(jsonData);
...
}
dynamicObject will contains all of your json.
IINM, based on the data you're getting, your AwsSignatureRequestViewModel should look something like this:
public class AwsSignatureRequestViewModel
{
public DateTime expiration { get; set; }
public List<Dictionary<string, string>> conditions { get; set; }
}
conditions is an "array of objects".
The way you currently have your model, data would map to something like this (which isn't the case):
{
"expiration": "2015-06-14T21:02:52.969Z",
"conditions":
{
"acl": "private",
"bucket": "anyoldbucket",
....
},
.....
Hth...
I need to pass a complex object representing a data filter to an action using GET which returns a filtered data set in a csv file.
The filter object is something like this on the client (much more complex in actuality, simplified for brevity):
var filter = {
Folders = [
{ Positive: true, Reference: { Id: 19, Name: "Container" } },
{ Positive: true, Reference: { Id: 37, Name: "Bullseye" } },
]
}
The corresponding server side classes look something like this:
public class MyFilter
{
public List<MyComparison> Folders { get; set; }
}
public class MyComparison
{
public bool Positive { get; set; }
public MyReference Reference { get; set; }
}
public class MyReference
{
public int Id { get; set; }
public string Name {get; set; }
}
My action looks like this:
[HttpGet]
public FileContentResult Export(MyFilter filter, string sort, bool sortAscending)
{
string data = GetCsvData(filter, sort, sortAscending);
return this.File(StrToByteArray(data), "text/csv", "Data.csv");
}
I have tried calling this action from javascript like this:
function exportFilter(aFilter) {
var params = { filter: aFilter, sort: "Name", sortAscending: true };
var destination = "MyController/Export?" + decodeURIComponent($.param(params));
document.location = destination;
}
Within the action, both the sort and sortAscending parameters are properly populated. The filter is an object of type MyFilter, but its Folders property is null.
Is ASP.NET MVC incapable of properly deserializing complex parameters in this way (ie in the context of a GET)? What is the correct way to address this problem?
The databinding algorithm in Asp.net MVC is not very good in deserializing complex inputs in .NET types. In the best scenario, you would get some strange results and/or slow performance compared to other dedicated solutions. In this case, Json.NET provides much better results at deserializing json data in .NET types, and its very fast too.
You should pass these filters like an ordinary string parameter and, inside the action, deserialize it using Json.NET. Something like this:
using Newtonsoft.Json;
public ActionResult MyAction(string myFilters)
{
var deserializedObject = JsonConvert.DeserializeObject(myFilters);
}
It can bind complex objects/parameters, the problem is the way the params are being sent. For example, you're sending:
http://localhost/Home/Export?filter[Folders][0][Positive]=true&filter[Folders][0][Reference][Id]=19&filter[Folders][0][Reference][Name]=Container&filter[Folders][1][Positive]=true&filter[Folders][1][Reference][Id]=37&filter[Folders][1][Reference][Name]=Bullseye&sort=Name&sortAscending=true
But the MVC model binder expects this format:
http://localhost/Home/Export?filter.Folders[0].Positive=true&filter.Folders[0].Reference.Id=19&filter.Folders[0].Reference.Name=Container&filter.Folders[1].Positive=true&filter.Folders[1].Reference.Id=37&filter.Folders[1].Reference.Name=Bullseye&sort=Name&sortAscending=true
I'm not sure of the easiest way to build a string matching that pattern from a javascript object though.