Bind array of complex objects in GET request - jquery.param() traditional flag - javascript

I'm trying make a jquery.ajax call to the following controller action:
public ActionResult Handler(SemanticPart[] semanticParts, string first, string last)
I have the following data JSON object with the corresponding server side model having public { get; set; } properties:
var data = {
semanticParts: [{ hasLabel: "label", hasType: "type", hasIndex : 0 }],
first: "first",
last : "last"
};
The problem is jQuery.param seems to have no serialization option for the default MVC model binder.
decodeURIComponent($.param(data)) produces:
"semanticParts[0][hasLabel]=label&semanticParts[0][hasType]=type&semanticParts[0][hasIndex]=0&first=first&last=last"
while setting the traditional flag like so decodeURIComponent($.param(data, true)) produces:
"semanticParts=[object+Object]&first=first&last=last"
MVC's default model binder for complex arrays needs the following to bind correctly (tested in Fiddler Composer):
"semanticParts[0].hasLabel=label&semanticParts[0].hasType=type&semanticParts[0].hasIndex=0&first=first&last=last"
Which simply used: array[0].property= instead of array[0][property]=
I understand there's no universal specification for param strings agreed-upon by all web frameworks but why jQuery.param with traditional flag set true returns a [object+Object] is beyond me... that is absolutely useless in any framework.
It there any way to patch this?
Perhaps a regex to replace the pattern [#][text] with [#].text? (actually the encoded version of this is more relevant)

You should implement a model to hold this parameters:
public class SemanticPartViewModel
{
public List<SemanticPart> semanticParts { get; set;}
public string first { get; set; }
public string last { get; set;}
}
And change the controller to receive this as parameter
public ActionResult Handler(SemanticPartViewModel semanticParts)
{
/*Do stuff*/
}
You should use JSON.stringify(mydata) as well to create the data for the $.ajax call
This way the default modelbinder will take care about the rest.

Related

Access JavaScript objects within an Array using C#

I have an array filled with objects in JavaScript. I passed the entire array to an API. How can I access an object's properties, using C#?
Define C# model class that matches JavaScript objects, accept array of this object in your controller and you will have the objects to use within controller. JavaScript objects have to be sent in JSON format.
A. JSON in C# generally
Here are popular choices for interacting with json in .NET:
(.NET Core) System.Text.Json.JsonSerializer. Plase see How to serialize and deserialize JSON in .NET
JSON.NET with models. Please see Serializing and Deserializing JSON
JSON.NET's Linq to JSON. Please see LINQtoJSON or Querying JSON with LINQ
B ASP.NET example
Check out Examine the PostTodoItem create method in Tutorial: Create a web API with ASP.NET Core.
I shows you how to consume a json payloads.
Json
{
"name":"walk dog",
"isComplete":true
}
Model
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
}
Controller action
// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
//return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}

Pass collection of objects inside an object from js to C#

This question can be very easy for you but I cannot find the solution anywhere, I've searched on SO as well. How can I send a collection of objects inside an object to a C# API method, here is my code that I try to send
{
"Question":"sdvsdv",
"PollQuestions":
[
{
"QuestionText":"Question Text"
}
]
}
I've tried with Advanced REST client as well (not only from JS), I can see value of parent object's parameters but PollQuestions objects parameters are all empty.
Please suggest a work around. Its been couple of days and I cannot get through it. Thanks in advance.
EDIT:
Here is how API method looks like:
Advanced Rest client request:
JS request:
Poll is a simple class like this:
[Serializable]
[DataContract(Namespace = "")]
public class Poll
{
[DataMember]
public string Question
{
get;
set;
}
[DataMember]
public Collection<PollQuestion> PollQuestions { get; set; }
}
PollQuestion object:
[Serializable]
public class PollQuestion : AnotherClass
{
public string QuestionText
{
get;
set;
}
}
In your case if the problem is to Map the request JSON in C# then try below code:
Class structure:
public class PollQuestion
{
public string QuestionText { get; set; }
}
public class RootObject
{
public string Question { get; set; }
public List<PollQuestion> PollQuestions { get; set; }
}
In Post use [FromBody] attribute to map JSON to C# class:
public void YourMethod([FromBody] RootObject data)
{
// your code to use request data
}
If you are using WebAPI version="5.2.3" (Or might be supported in the lower version also) no need to serialize requested JSON to C# class. Use this to generate C# classes for JSON.
For JSON structure C# treats,
this:
{
}
as Object and
this:
[]
to List<> OR Array.
I set up a simple sample project in .NET Core and it seems to be working fine for me with your JSON data.
These are the classes I used:
public class PollQuestion
{
public string QuestionText { get; set; }
}
public class Poll
{
public string Question { get; set; }
public List<PollQuestion> PollQuestions { get; set; }
}
And here is the API call function:
[HttpPost]
public void Post([FromBody]Poll value)
{
}
When I make a postman request the Poll class is filled as expected.
Here is also a link to my sample project: https://bitbucket.org/alleskapaul/ld42/downloads/JsonExample.zip
Could you change your models in your project to match mine and see if that works? My guess would be that your model is incorrect as the JSON request works for me as well. If it is not working could you upload a sample project so I can see if I can modify it to work?
I was inheriting a class that was using [DataContract] and [DataMember] attributes, so I had to use DataMember for all parameters of the child class as well.
Thanks everyone for your answers but [FromBody] is not required in an API method when parameter is of complex type.
It works without DataContract and DataMember as well if you have not used DataMember anywhere in the class and also not inheriting any class that uses it.
You can get more info here: When to use DataContract and DataMember attributes?

Serialize form to string and deserealize it later to complex view model

I'm implementing the "Save Draft" functionality on my dynamically generated page and trying to make it as generic as possible. All my controllers and pages should support it and that's why I thought about creating a SaveDraft() POST action in my base controller which will receive a serialized form as a string which can be directly saved into the database and deserialized to a view model later in the specific get action using specific view model
[HttpPost]
public ActionResult SaveDraft(string jsonData, long id)
My first idea was to create the generic base controller and pass the view model type to it but the problem is that some controllers have multiple differently named POST actions and using different view model types, I cant change it now.
Some view models are complex and looking like
public class CollateralsDataModel
{
//...
public List<Applicant> Applicants { get; set; }
}
public class Applicant
{
public long ApplicantId { get; set; }
public IList<RealEstateSecurityCollateralsDTO> RealEstateSecurityCollaterals { get; set; }
public IList<AdditionalCollateralDTO> AdditionalCollaterals { get; set; }
}
public class RealEstateSecurityCollateralsDTO
{
[Required]
[Display(ResourceType = typeof(CollateralsData), Name = nameof(CollateralsData.RealEstateSecurityType))]
public int? RealEstateSecurityTypeId { get; set; }
//...
}
The input names on the form are looking like
"Applicants[0].MortgageApplicantId": "11595",
"Applicants[0].RealEstateSecurityCollaterals[0].Id": "17",
"Applicants[1].MortgageApplicantId": "11596",
"Applicants[1].RealEstateSecurityCollaterals.Index": "0",
"Applicants[1].AdditionalCollaterals[0].Id": "138",
"Applicants[1].AdditionalCollaterals[0].AdditionalCollateralTypeId": "4",
My question is - how can I serialize them to the string so I can deserialize it later?
I tried using different combinations of
$('.draft-data-form').serializeArray()
$('.draft-data-form').serialize()
JSON.stringify($('.draft-data-form').serializeArray());
but in my Action I get the flat JSON structure and I can't deserialize it
var obj = JsonConvert.DeserializeObject<CollateralsDataModel>(jsonData);
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type '...CollateralsDataModel' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
I've also tried to deserialize it as a list but then I get the List of 115 entries
var obj = JsonConvert.DeserializeObject<List<CollateralsDataModel>>(jsonData);
So my question is - how can I serialize them to the string so I can deserialize it later?
You do not want to serialize/deserialize a List. Only a single instance of the model.
I would guess that the error is in instantiating the list within the model. I know that the DataContractSerializer has an issue where if the List is not instantiated to an empty list when the model is constructed then the deserialization is unable to add to it.
Not sure if this is the same problem you are facing but could be worth a try.
eg. use:
public List<Applicant> Applicants { get; set; } = new List<Applicant>();

how to serialize object into json using c#

I tried using
var myobject = JsonConvert.SerializeObject(Customer);
but problem is in Customer properties are like
FirstName and my service expecting json input like firstName
{"firstName":"Neo"}
statement JsonConvert.SerializeObject(Customer); gives me {"FirstName":"Neo"} which is wrong.
So How can I change first letter when JsonConvert.SerializeObject happened ?
Or How to take only one parameter as input json firstname instead if using Customer object.
You should use the Json.NET attribute support to customize the naming:
public class Customer
{
[JsonProperty("firstName")]
public string FirstName { get; set; }
}
You can define how data need to be serialized.
When using webapi, we can define a CamelCasePropertyNamesContractResolver (part of json.net library) as formatter in the register method of the webapi config.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
The code above is especially for webapi, nevertheless I believe a simular approach can be a solution when not using webapi.
You can use like this. Use DataMember property, it'll serailize as mentioned.
[DataContract(Namespace = "")]
public class Customer
{
[DataMember(Name = "firstName")]
public string FirstName { get; set; }
}

JSON representation of a list of keyvaluepairs

I have an MVC JSON controller method that I call from frontend. It looks like this:
public JsonResult FacetedSearch(string searchString, List<KeyValuePair<string,string>> facets)
I'm calling it via jQuery ajax at the frontend, I'm serializing the data in the following manner:
JSON.stringify({searchString: "the matrix", facets: [{Key: "TypeName", Value: "Feature Film"}, {Key:"TypeName", Value:"Series"}]}
When I debug through my application code, I see that searchString gets passed successfully over to the MVC method, but the variable facets gives me a list of 2 KeyValuePairs with null Key and Value.
I've looked at my serialization and it seems valid but for whatever reason it isn't going over to my application correctly. What gives?
Rather than expect two objects in your signature, it would make more sense to expect a single object that contains both of your parameters. This would be something like the following.
public JsonResult FacetedSearch(RequestObject requestObject)
{ }
public class RequestObject
{
public string searchString { get; set; }
public List<KeyValuePair<string,string>> facets { get; set; }
}
This way, when you send your JSON object, the signature is an object with two properties, just like the object that you are sending.
Per Is there a serializable generic Key/Value pair class in .NET?
I found out why it's not serializing it. Apparently it's unserializable.

Categories

Resources