How to post an array of complex objects with JSON, jQuery to ASP.NET MVC Controller?
Asked Answered
S

7

92

My current code looks like the following. How can I pass my array to the controller and what kind of parameters must my controller action accept?

function getplaceholders() {
    var placeholders = $('.ui-sortable');
    var result = new Array();
    placeholders.each(function() {
        var ph = $(this).attr('id');
        var sections = $(this).find('.sort');
        var section;

        sections.each(function(i, item) {
            var sid = $(item).attr('id');

            result.push({ 'SectionId': sid, 'Placeholder': ph, 'Position': i });
        });
    });
    alert(result.toString());
    $.post(
        '/portal/Designer.mvc/SaveOrUpdate',
        result,
        function(data) {
            alert(data.Result);
        }, "json");
};

My controller action method looks like

public JsonResult SaveOrUpdate(IList<PageDesignWidget> widgets)
Scammony answered 26/11, 2008 at 10:56 Comment(0)
S
84

I've found an solution. I use an solution of Steve Gentile, jQuery and ASP.NET MVC – sending JSON to an Action – Revisited.

My ASP.NET MVC view code looks like:

function getplaceholders() {
        var placeholders = $('.ui-sortable');
        var results = new Array();
        placeholders.each(function() {
            var ph = $(this).attr('id');
            var sections = $(this).find('.sort');
            var section;

            sections.each(function(i, item) {
                var sid = $(item).attr('id');
                var o = { 'SectionId': sid, 'Placeholder': ph, 'Position': i };
                results.push(o);
            });
        });
        var postData = { widgets: results };
        var widgets = results;
        $.ajax({
            url: '/portal/Designer.mvc/SaveOrUpdate',
            type: 'POST',
            dataType: 'json',
            data: $.toJSON(widgets),
            contentType: 'application/json; charset=utf-8',
            success: function(result) {
                alert(result.Result);
            }
        });
    };

and my controller action is decorated with an custom attribute

[JsonFilter(Param = "widgets", JsonDataType = typeof(List<PageDesignWidget>))]
public JsonResult SaveOrUpdate(List<PageDesignWidget> widgets

Code for the custom attribute can be found here (the link is broken now).

Because the link is broken this is the code for the JsonFilterAttribute

public class JsonFilter : ActionFilterAttribute
{
    public string Param { get; set; }
    public Type JsonDataType { get; set; }
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.ContentType.Contains("application/json"))
        {
            string inputContent;
            using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream))
            {
                inputContent = sr.ReadToEnd();
            }
            var result = JsonConvert.DeserializeObject(inputContent, JsonDataType);
            filterContext.ActionParameters[Param] = result;
        }
    }
}

JsonConvert.DeserializeObject is from Json.NET

Link: Serializing and Deserializing JSON with Json.NET

Scammony answered 26/11, 2008 at 13:11 Comment(4)
Looks great - The blog post and custom attribute code links don't work anymore - can you repost?Getup
This solution requires changes on the client and server side. I know you needed this long ago, but I may as well provide a link to a different approach, that uses a simple jQuery plugin that makes it possible to convert whatever Javascript object into a form that default model binder understands and model binds to parameters. No filters needed. erraticdev.blogspot.com/2010/12/… I don't know how you solved validation errors but I have a solution for that as well: erraticdev.blogspot.com/2010/11/…Oly
Can you give the source/origin of JavaScriptConvert.DeserializeObject ?Freeway
It is the Newtonsoft Json library - if you open up nuget package manager and search on Newtonsoft, it will show up for you (now that it is 2016). Probably this is now obvious, but in case anyone is wondering.Gratt
G
22

Action Filters, jquery stringify, bleh...

Peter, this functionality is native to MVC. That's one of things that makes MVC so great.

$.post('SomeController/Batch', { 'ids': ['1', '2', '3']}, function (r) {
   ...
});

And in the action,

[HttpPost]
public ActionResult Batch(string[] ids)
{
}

Works like a charm:

enter image description here

If you're using jQuery 1.4+, then you want to look into setting traditional mode:

jQuery.ajaxSettings.traditional = true;

As described here: http://www.dovetailsoftware.com/blogs/kmiller/archive/2010/02/24/jquery-1-4-breaks-asp-net-mvc-actions-with-array-parameters

This even works for complex objects. If you're interested, you should look into the MVC documentation about Model Binding: http://msdn.microsoft.com/en-us/library/dd410405.aspx

Garden answered 8/9, 2011 at 21:6 Comment(4)
you may be right, but JSON model binder is new to MVC3 and the question was asked in 2008 when this wasn't supported. It would be worth mentioning in your answer.Cooksey
How is this an example of passing an array of complex objects?Forethought
It's not but the example still applies (MVC 3+). So long as your parameter names match up with the model you're expecting you will not have any issue.Bindman
The key here is to create a JSON object with the method parameter name ("ids") and then put the array of complex objects within that. Additionally, make the third parameter "true" and you'll take care of traditional mode.Extensity
B
12

In .NET4.5, MVC 5 no need for widgets.

Javascript:

object in JS: enter image description here

mechanism that does post.

    $('.button-green-large').click(function() {
        $.ajax({
            url: 'Quote',
            type: "POST",
            dataType: "json",
            data: JSON.stringify(document.selectedProduct),
            contentType: 'application/json; charset=utf-8',
        });
    });

C#

Objects:

public class WillsQuoteViewModel
{
    public string Product { get; set; }

    public List<ClaimedFee> ClaimedFees { get; set; }
}

public partial class ClaimedFee //Generated by EF6
{
    public long Id { get; set; }
    public long JourneyId { get; set; }
    public string Title { get; set; }
    public decimal Net { get; set; }
    public decimal Vat { get; set; }
    public string Type { get; set; }

    public virtual Journey Journey { get; set; }
}

Controller:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Quote(WillsQuoteViewModel data)
{
....
}

Object received:

enter image description here

Hope this saves you some time.

Batt answered 15/1, 2015 at 12:15 Comment(0)
D
8

Towards the second half of Create REST API using ASP.NET MVC that speaks both JSON and plain XML, to quote:

Now we need to accept JSON and XML payload, delivered via HTTP POST. Sometimes your client might want to upload a collection of objects in one shot for batch processing. So, they can upload objects using either JSON or XML format. There's no native support in ASP.NET MVC to automatically parse posted JSON or XML and automatically map to Action parameters. So, I wrote a filter that does it."

He then implements an action filter that maps the JSON to C# objects with code shown.

Dumpish answered 26/11, 2008 at 13:6 Comment(1)
I was just writing my anwser. But I will post it anyway ;-)Scammony
S
7

First download this JavaScript code, JSON2.js, that will help us serialize the object into a string.

In my example I'm posting the rows of a jqGrid via Ajax:

    var commissions = new Array();
    // Do several row data and do some push. In this example is just one push.
    var rowData = $(GRID_AGENTS).getRowData(ids[i]);
    commissions.push(rowData);
    $.ajax({
        type: "POST",
        traditional: true,
        url: '<%= Url.Content("~/") %>' + AREA + CONTROLLER + 'SubmitCommissions',
        async: true,
        data: JSON.stringify(commissions),
        dataType: "json",
        contentType: 'application/json; charset=utf-8',
        success: function (data) {
            if (data.Result) {
                jQuery(GRID_AGENTS).trigger('reloadGrid');
            }
            else {
                jAlert("A problem ocurred during updating", "Commissions Report");
            }
        }
    });

Now on the controller:

    [HttpPost]
    [JsonFilter(Param = "commissions", JsonDataType = typeof(List<CommissionsJs>))]
    public ActionResult SubmitCommissions(List<CommissionsJs> commissions)
    {
        var result = dosomething(commissions);
        var jsonData = new
        {
            Result = true,
            Message = "Success"
        };
        if (result < 1)
        {
            jsonData = new
            {
                Result = false,
                Message = "Problem"
            };
        }
        return Json(jsonData);
    }

Create a JsonFilter Class (thanks to JSC reference).

    public class JsonFilter : ActionFilterAttribute
    {
        public string Param { get; set; }
        public Type JsonDataType { get; set; }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.HttpContext.Request.ContentType.Contains("application/json"))
            {
                string inputContent;
                using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream))
                {
                    inputContent = sr.ReadToEnd();
                }
                var result = JsonConvert.DeserializeObject(inputContent, JsonDataType);
                filterContext.ActionParameters[Param] = result;
            }
        }
    }

Create another class so the filter can parse the JSON string to the actual manipulable object: This class comissionsJS are all the rows of my jqGrid.

    public class CommissionsJs
    {
        public string Amount { get; set; }

        public string CheckNumber { get; set; }

        public string Contract { get; set; }
        public string DatePayed { get; set; }
        public string DealerName { get; set; }
        public string ID { get; set; }
        public string IdAgentPayment { get; set; }
        public string Notes { get; set; }
        public string PaymentMethodName { get; set; }
        public string RowNumber { get; set; }
        public string AgentId { get; set; }
    }

I hope this example helps to illustrate how to post a complex object.

Sadism answered 2/2, 2011 at 18:38 Comment(0)
D
-1
    [HttpPost]
    public bool parseAllDocs([FromBody] IList<docObject> data)
    {
        // do stuff

    }
Dexamethasone answered 9/8, 2016 at 14:16 Comment(0)
G
-2

No need to do anything special. only in your post section do as follows:

    $.post(yourURL,{ '': results})(function(e){ ...}

In server use this:

   public ActionResult MethodName(List<yourViewModel> model){...}
Girard answered 27/2, 2019 at 6:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.