MVC 4 Post on Nested JSON is null - c#

I'm currently working on a mobile app that communicates with an MVC4 API.
The problem I just noticed is that it seems to be unable to parse nested objects for some reason.
Example:
I'm POSTing the following data towards the url (http://localhost/DoSomething):
{
"id":610,
"dynamic":[
{
"fieldId":2756,
"fieldValue":""
},
{
"fieldId":2757,
"fieldValue":""
}
],
"person":{
"name":"test",
"age":"123",
"dateOfBirth":"test",
"groups":[
{
"groupId":1182
},
{
"groupId":1311
},
{
"groupId":673
}
]
}
}
Knowing that MVC will try to serialize it against the provided models, I have created the following model for the request:
public class PersonRequest : RequestBase
{
public class Field
{
public int fieldId { get; set; }
public string fieldValue { get; set; }
}
public class Group
{
public int groupId { get; set; }
}
public class Person
{
public string name { get; set; }
public string age { get; set; }
public string dateOfBirth { get; set; }
public IEnumerable<Group> groups { get; set; }
}
public int id { get; set; }
public IEnumerable<Field> dynamic { get; set; }
public Person person { get; set; }
}
In order to handle the input I have created the following route (which works):
routes.MapRoute(
name: "PersonRequest",
url: "DoSomething",
defaults: new { controller = "Person", action = "Generate" }
);
And my actual routing method:
[HttpPost]
public ActionResult Generate(PersonRequest request)
{
return Json(request, JsonRequestBehavior.AllowGet);
}
The response is however:
{"id":610,"person":null,"dynamic":null}
After searching for a possible solution, people said you would have to use IEnumerable for such situations instead of a List. Sadly, this didn't seem to be working for me.
Just some extra info:
I could always use JSON.stringify on the clientside on the dataobject person and dynamic, and eventually deserialize it myself on the backend (as shown in this topic: parse Json text to C# object in asp mvc 4), but there has to be a better workaround for this problem.
Change: Changed groups to dynamic in the resulting json.
Solved: https://stackoverflow.com/a/29349804/2076351

Why are you using IEnumerable<>, the query is fired at the end,
use List<> and check.
public class PersonRequest : RequestBase
{
public class Field
{
public int fieldId { get; set; }
public string fieldValue { get; set; }
}
public class Group
{
public int groupId { get; set; }
}
public class Person
{
public string name { get; set; }
public string age { get; set; }
public string dateOfBirth { get; set; }
public List<Group> groups { get; set; }
}
public int id { get; set; }
public List<Field> dynamic { get; set; }
public Person person { get; set; }
}

Solved.
Seems that my backend was properly setup. The fix was the way the data was sent towards the backend.
If you are sending a nested object towards the backend, to be parsed. You'll have to do two things:
Use JSON.stringify on the entire data send to the data
Use a proper content-type, e.g.:
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.setRequestHeader("hash", params.hash);
xhr.send(JSON.stringify(params.data));

Related

Post complex model angularjs to web api

Let's say I have this models
public class HouseHolds
{
public string Code { get; set; }
public List<HouseHoldBeneficiaries> HouseHoldBeneficiaries { get; set; }
}
public class HouseHoldBeneficiaries
{
public Guid HouseHoldId { get; set; }
public Guid BeneficiaryId { get; set; }
public int HouseHoldRelationShipId { get; set; }
}
I want to post new HouseHold to database so I send the result from angularjs
but I Can't bind List HouseHoldBeneficiaries.
Object
Code: "wer"
HouseHoldBeneficiaries:
HouseHoldRelationShipId: 2
BeneficiaryId: EAFC7940-5FAD-4C04-BC01-6D052BA5E711
But my HouseHoldBeneficiaries is always null in the model ??
Your HouseHoldBeneficiaries is a collection and thus needs to be send as json array like
{
Code:"wer",
HouseHoldBeneficiaries: [
{
HouseHoldRelationShipId: 2,
BeneficiaryId: EAFC7940-5FAD-4C04-BC01-6D052BA5E711
}
]
}

nested json deserialization - need another set of eyes

I have been trying to get this json to deserialize for two days now using RestSharp. I have gone through the RestSharp github site, looked at countless examples, and spent much time here on Stack Overflow to try and find the answer to no avail. My code had previously worked perfectly but the vendor changed their API version and I was forced to do an update to keep using the application for my legal practice. My json is as follows(client info has been removed and replaced with generic info):
{
"data":[
{
"id":1035117666,
"client":
{
"id":905422394,
"name":"client1"
},
"display_number":"11-00012",
"description":"General",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2011-12-14",
"close_date":null,
"billing_method":"hourly"
},
{
"id":1035117768,
"client":
{
"id":905422506,
"name":"client2"
},
"display_number":"12-00037",
"description":"HOA",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2012-08-07",
"close_date":null,
"billing_method":"hourly"
}
],
"meta":
{
"paging":
{
"next":"https://app.goclio.com/api/v4/matters.json?fields=id%2C+client%7Bid%2C+name%7D%2C+display_number%2C+description%2C+practice_area%7Bid%2C+name%7D%2C+status%2C+open_date%2C+close_date%2C+billing_method&limit=2&page_token=BAh7BjoLb2Zmc2V0aQc%3D--b1ea3eba20c8acefbcdfc7868debd1e0ee630c64&status=Open"
},
"records":91
}
}
I built the following schema within my c# code:
public class MatterList
{
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
public class Meta
{
public Paging paging { get; set; }
public int records { get; set; }
}
public class Paging
{
public string previous { get; set; }
public string next { get; set; }
}
[DeserializeAs(Name = "data")]
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
public class PracticeArea
{
public int id { get; set; }
public string name { get; set; }
}
public class Client
{
public int id { get; set; }
public string name { get; set; }
}
When I run the RestSharp deserialize method I am sending the result to an object of type MatterList using the following line of code
MatterList matterList = jsonHandler.Deserialize<MatterList>(response);
I have so far attempted to deserialize without the Meta or Paging POCO classes with the accompanying change to the MatterList class (taking out the Meta property).
I have tried with and without the [DeserializeAs(Name="data")] directive.
I have tried to set the RootElement of the json response prior to deserialization.
I have tried to shorthand the deserialization by combining it with the Execute request code
IRestResponse<MatterList> matterList = client.Execute<MatterList>(request);
I have created a container class called MatterContainer which I placed between MatterList and Matter classes in the schema:
public class MatterList
{
public List<MatterContainer> matters { get; set; }
}
public class MatterContainer
{
public Matter matter { get; set; }
}
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
I know I am getting the json response back from the server correctly so my request is proper and MatterList is not null after deserialization. The problem is that I cannot get the deserialization to actually populate the List matters within the MatterList class.
I have been looking at this off and on for two days and cannot get past this hurdle. If anyone sees what I did wrong I would greatly appreciate the insight, I am at a point where I cannot progress further with my application.
Thanks!
I think your [DeserializeAs(Name = "data")] attribute is in the wrong place. Try putting it in the root class instead:
public class MatterList
{
[DeserializeAs(Name = "data")]
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
alternatively, try renameing that property to data

Web Api Parameter binding Inconsistent

After several hours of googling and attempting to figure this out, I still can't figure this out. The issue is that the "[FromBody] Route input" parameter for the create method will for some reason bind Route.RouteMember[0].Lab to the provided data, but it will set Route.RouteMember[0].RouteMemberType to null. (This will happen for all items in the RouteMember array, not just the first item) Why isn't RouteMemberType bound to the input, but yet the input it will bind to lab? I must admit that I have sometimes seen it bind to a RouteMemberType but with no discernible cause.
Relevant WebApiConfig
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
json.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
RouteController
[HttpPost]
public Route Create([FromBody] Route input) {
UnitOfWork.Routes.Add(input);
UnitOfWork.SaveChanges();
return input;
}
Route
public class Route : BaseModel {
public String Name { get; set; }
public String ShortName { get; set; }
public virtual ICollection<RouteMember> RouteMembers { get; set; }
}
RouteMember
public class RouteMember : BaseModel {
[ForeignKey("Route")]
public int RouteId { get; set; }
public virtual Route Route { get; set; }
[ForeignKey("Lab")]
public int LabId { get; set; }
public virtual Lab Lab { get; set; }
[ForeignKey("RouteMemberType")]
public int RouteMemberTypeId { get; set; }
public virtual RouteMemberType RouteMemberType { get; set; }
}
Lab
public class Lab : BaseModel {
public String Name { get; set; }
public String ShortName { get; set; }
public String Description { get; set; }
[ForeignKey("Building")]
public int BuildingId { get; set; }
public virtual Building Building { get; set; }
}
RouteMemberType
public class RouteMemberType : BaseModel {
public String Name { get; set; }
}
Request Input
{
"id":0,
"name":"t1",
"shortName":"t1",
"routeMembers":[
{
"active":true,
"labId":11,
"lab":{
"$id":"1",
"building":null,
"name":"t1a",
"shortName":"t1a",
"description":"t1a",
"buildingId":2,
"id":11,
"active":true
},
"routeMemberTypeId":1,
"routeMemberType":{
"$id":"1",
"name":"Destintation",
"id":1,
"active":true
}
},
{
"active":true,
"labId":12,
"lab":{
"$id":"2",
"building":null,
"name":"t2a",
"shortName":"t2a",
"description":"t2a",
"buildingId":2,
"id":12,
"active":true
},
"routeMemberTypeId":2,
"routeMemberType":{
"$id":"2",
"name":"Fringe",
"id":2,
"active":true
}
}
],
"active":true
}
While I still have been unable to completely resolve this issue to my satisfaction, it appears in some part to be related to the
json.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
in the WebApiConfig. Turning object references off, allows the RouteMemberTypes to be bound. To work around this issue, I am using PreserveReferencesHandling.None and doing the referential integrity manually. I will update if I find a better solution

Server not receiving lists inside of JSON object passed in

These are the data contracts that are being used in the function.
public class ResumeSkillsListDataContract : IResumeSkillsListDataContract
{
public IList<ISkillDataContract> KnownSkillsList { get; set; }
public IList<ISkillDataContract> BadSkillsList { get; set; }
public IList<ISkillDataContract> NewSkillsList { get; set; }
public Int32 PersonId { get; set; }
}
public class SkillDataContract : ISkillDataContract
{
public String Name { get; set; }
public Nullable<Int32> Id { get; set; }
public Nullable<Boolean> IsAssigned { get; set; }
public Nullable<Int32> SkillCategoryId { get; set; }
public Nullable<Int32> SkillCategoryMappingId { get; set; }
}
This is the function in the controller. I am expecting three populated lists and a PersonId to be passed in. However, I am only receiving the PersonId. In my Post, I see the data I am expecting to see in the console but when debugging the controller, item.List is empty every time.
public IList<ISkillDataContract> PostResumePersonSkills(ResumeSkillsListDataContract item)
{
var newList = item.KnownSkillsList;
var ignoreList = item.BadSkillsList;
var existingList = item.NewSkillsList;
var personId = item.PersonId;
return resumePersonSkillsBusinessLibrary.PostSkills(newList, ignoreList, existingList, personId);
}
Here is a quick snapshot of what im sending to the server. Any idea what could be wrong? Thanks.
$scope.doneWithSkills = function () {
var resumeCollection = {
KnownSkillsList: $scope.KnownSkillsList, BadSkillsList: $scope.IgnoredSkillsList,
NewSkillsList: $scope.SaveAsSkillsList, PersonId:$scope.ParsedPerson.Person.PersonId
};
resumeParserService.PostResumeSkills(resumeCollection);
};
Function in the resumeParserService
self.PostResumeSkills = function (skills) {
var url = 'ResumeSkill/PostResumePersonSkills';
console.log(skills);
webApiService.Post(url, skills);
};
Sample JSON being passed.
{"KnownSkillsList":[{"Name":"C++","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":154},{"Name":"Unix","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":219},{"Name":".Net","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":139},{"Name":"Clearcase","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":155},{"Name":"Uml","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":218},{"Name":"Xml","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":239},{"Name":"Java","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":173},{"Name":"Python","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":199},{"Name":"Visual Basic","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":223}],"BadSkillsList":[],"NewSkillsList":[{"Name":"Algorithms","Id":null,"IsAssigned":null,"SkillCategoryId":3,"SkillCategoryMappingId":null}],"PersonId":1203}
I would expect this is caused by your lists ResumeSkillsListDataContract being lists of an interface. The problem is going to be that when the JSON is deserialized the deserializer does not know what concrete type to instantiate.
Try changing to this and see if it resolves the problem
public class ResumeSkillsListDataContract : IResumeSkillsListDataContract
{
public IList<SkillDataContract> KnownSkillsList { get; set; }
public IList<SkillDataContract> BadSkillsList { get; set; }
public IList<SkillDataContract> NewSkillsList { get; set; }
public Int32 PersonId { get; set; }
}

Problems Serializing form data and submitting it to the control with mapping

I have a form that is flexible. Meaning that depending on what selection you make in a dropdownlist the fields will be different. Also, the controller action that is called will also change. I am trying to get this to work with just a simple example, but I can't find how to submit data to the controller and have the controller map it correctly to a defined class.
Clarification: When a user creates a new question that has only one choice this is the form/controller that they are using. However, when they create a question with multiple choices I would like to use the same form/controller. The error i am getting is that the object is null. Which i think means that whenever the data is being passed to the controller, it is not being properly mapped into the object. How can i map the data explicitly into my defined object? Or should i do this whole thing differently?
Here is the controller:
[HttpPost]
public ActionResult CreateSimpleQuestion(SimpleQuestion question)
{
if (ModelState.IsValid)
{
question.question.is_counted = true;
question.question.DateCreated = DateTime.Now;
db.Questions.Add(question.question);
db.QuestionChoices.Add(question.choices[0]);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(question);
}
Here is the class:
[Serializable]
public class SimpleQuestion
{
public Question question { get; set; }
public QuestionChoices[] choices { get; set; }
}
Here is the script that is calling the controller action:
<script type="text/javascript">
$("form").on("submit", function (event) {
event.preventDefault();
var data = $('form').serialize();
console.log(data);
$.post('/Question/CreateSimpleQuestion/', data);
});
</script>
This is the serialized data:
QuestionTitle=faketitle&Keywords=fakekeywords&Description=fakedescription&Comments=fakecomments&QuestionType=Simple&DisplayText=fakequestiontext&OrderNumber=fakeorder
And in case you need the specifics of the model:
public class Question
{
public int QuestionId { get; set; }
public string QuestionTitle { get; set; }
public DateTime DateCreated { get; set; }
public string QuestionType { get; set; }
public string Keywords { get; set; }
public bool is_counted { get; set; }
public int? ParentId { get; set; }
[Column(TypeName = "ntext")]
[MaxLength]
public string Description { get; set; }
[Column(TypeName = "ntext")]
[MaxLength]
public string Comments { get; set; }
//These define a one to many relationship
public virtual ICollection<TeamQuestionRoster> TeamQuestionRosters { get; set; }
public virtual ICollection<Response> Responses { get; set; }
public virtual ICollection<QuestionChoices> QuestionChoices { get; set; }
}
public class QuestionChoices
{
public int QuestionChoicesId { get; set; }
public string DisplayText { get; set; }
public int OrderNumber { get; set; }
public bool is_correct { get; set; }
//These are the FK properties
public int QuestionId { get; set; }
//This defines the FK Relationships
public virtual Question Question { get; set; }
//These define a one to many relationship
public virtual ICollection<ResponseDetails> ResponsDetails { get; set; }
}
I think you might be having a issue with your media type. Try posting JSON like this:
$.post('/Question/CreateSimpleQuestion', data, function() { /* success callback */ }, 'application/json');
EDIT:
The $.post shorthand method might be expecting 'json' rather than 'application/json'. I typically use $.ajax instead.
TAKE 2:
Based the JSON you posted, I can see that your data is not being serialized properly. You're getting name/value pairs instead of actual JSON objects. Your JSON data should like this:
{
"QuestionId" : 0,
"QuestionTitle" : "My Title",
"Description": "My Description"
}
Here's another SO post explaining how to convert the jQuery serialize results to an appropriate JSON object: Convert form data to JavaScript object with jQuery
Hope that helps :)

Categories

Resources