4

I have a server side object in my MVC code that looks like this:

class Person
{
  public long Id { get; set; }

  public string Name { get; set; }

  public IList<Friend> Friends { get; set; } 
}

class Friend
{
  public long Id { get; set; }

  public string Name { get; set; }
}

From the jQuery client, I am making an ajax request and sending the following data (line breaks introduced for readability):

var personId = $(...
var personName = $(...

var friends = []; // array of associative arrays

for (var i = 0; i < _friends.length; i++) // _friends is is a global array object and is different from friends below
{
  var friendId = $(...
  var friendName = $(...)

  friends[i] = { 'Id' : friendId, 'Name' : friendName };
}

... and so on to get values into variables

Please observe that I am sending what should go into Person.Friends, the IList<T> as a JavaScript array where each element of the array is an associative list that has properties just like the server side Friend class.

Here's my ajax request:

$.ajax('/Strings/JsonCreateNew', 
  { 
    cache: false, async: false, type: 'POST', 
    data: { 
        'Id' : personId, 
        'Name' : personName, 
        'Friends' : friends}, 
    dataType: 'json', 
    error: SaveNewFriendClientSideHandler.OnError, 
    success: SaveNewFriendClientSideHandler.OnSuccess 
  });

In my controller, as long as I have a FormCollection to receive the data, I get all my data, but I'd like to bind to the strongly typed Person object, so if I change my controller definition from:

[HttpPost]
public ActionResult JsonCreateNew(FromCollection person)
{
  // I get all the data in the person.AllKeys property
  return new EmptyResult();
}

to:

[HttpPost]
public ActionResult JsonCreateNew(Person person)
{
  // the default ASP.NET MVC model binder isn't able to bind to this
  // in fact, the routing module does not even route the request to this
  // action, so this action never gets fired.
  return new EmptyResult();
}

I still do get all my data but I am really still concerned about the IList<Friend>.

It is received in the Person object as an array that has the correct number of Friends objects with the right property names (Name and Id) but the values of both the properties of each Friend object are null or 0, i.e. not initialized even when there are values being sent by the client.

Please help.

Water Cooler v2
  • 32,724
  • 54
  • 166
  • 336

3 Answers3

9

There is a bug in MVC they refuse to fix that if the colection property name begins with the type name it does not bind. Change

    public IList<Friend> Friends { get; set; } 

To:

    public IList<Friend> Biscuits { get; set; } 

just temporary. It will work.

regisbsb
  • 3,664
  • 2
  • 35
  • 41
0

You have to specify the content type: contentType: "application/json". @MohammadRB solution contains it, but it should work even if you don't stringify the post data.

mhangan
  • 205
  • 2
  • 8
-1

I think it will work:

var p = { 'Id' : personId, 'Name' : personName, 'Friends' : friends };
$.ajax('/Strings/JsonCreateNew', 
  { 
    type: 'POST', 
    data: JSON.Stringify({ person : p }), 
    contentType : "application/json",
    dataType: 'json', 
    error: SaveNewFriendClientSideHandler.OnError, 
    success: SaveNewFriendClientSideHandler.OnSuccess 
  });

Also i prefer IEnumerable<> instead of IList<> if you only want iterate over them

Edit Test case :

var friends = new Array();
friends.push({ Id: "1", Name: "A" });
friends.push({ Id: "2", Name: "B" });
var p = { 'Id': 5, 'Name': "Mohammad", 'Friends': friends };

$(document).ready(function () {

  $.ajax('/Home/JsonCreateNew',
  {
      type: 'POST',
      data: JSON.stringify({ person: p }),
      contentType: "application/json",
      dataType: 'json',
      error: function () { alert("f"); },
      success: function () { alert("s"); }
  });

});
MRB
  • 3,752
  • 4
  • 30
  • 44
  • That doesn't work at all. In fact, that even broke the sending of the ajax request. – Water Cooler v2 Oct 10 '13 at 15:21
  • @WaterCoolerv2 I add a test and it work and i get person object in serverside action – MRB Oct 10 '13 at 15:44
  • @WaterCoolerv2 You must add 'contentType: "application/json"' and 'JSON.stringify()' to your ajax request, when i delete them, i also receive null values on server – MRB Oct 10 '13 at 15:53
  • For me, the console reports *JSON.Stringify is not a function*. Is there something I have to include to get the JSON object? – Water Cooler v2 Oct 10 '13 at 15:56
  • 2
    @WaterCoolerv2 What browser are you using? (are you using lower case of s in stringify. it may be a typo mistake) – MRB Oct 10 '13 at 16:05
  • I was stupid. Thanks, I figured that wrong capitalization out when I posted my last comment. Thanks much. However, now even though the JSON.stringify function works, the data received at the server end is still an empty object, whether I receive a `FormCollection` or a `Person` object. – Water Cooler v2 Oct 10 '13 at 16:09
  • @WaterCoolerv2 Sorry i can't figure out your problem. If you collect your data from form, why don't you use serialize method on your form? – MRB Oct 10 '13 at 17:01
  • Maybe this worked in 2013... If you want a working sample in 2020 check out: https://stackoverflow.com/a/33947968/2484903 – Jack Miller Dec 09 '20 at 08:21