1

I have been trying all the daylong to build a model in asp.net mvc3 for handling this json post. I have looked at most of stackoverflow'post but still couldn't succeed building it. The json data is as below

{"form":[{
          "ora1":{"start":"08:00:00","end":"08:50:00"},
          "ora2":{"start":"09:00:00","end":"09:50:00"},
          "ora3":{"start":"10:00:00","end":"10:50:00"},            
          "...":{"start":"12:10:00","end":"13:00:00"},
          "oran":{"start":"12:10:00","end":"13:00:00"}
         }]}

or even

 {"form":
    {"Monday":
          {
             "ora1":{"start":"08:00:00","end":"08:50:00"},
             "ora2":{"start":"09:00:00","end":"09:50:00"},
             "ora3":{"start":"10:00:00","end":"10:50:00"},
             "....":{"start":"11:10:00","end":"12:00:00"},
             "oran":{"start":"12:10:00","end":"13:00:00"}
            }
     },
     {"Tuesday":
          {
             "ora1":{"start":"08:00:00","end":"08:50:00"},
             "ora2":{"start":"09:00:00","end":"09:50:00"},
             "ora3":{"start":"10:00:00","end":"10:50:00"},
             "....":{"start":"11:10:00","end":"12:00:00"},
             "oran":{"start":"12:10:00","end":"13:00:00"}
            }
     }
}

Any kind of help will be appreciated. Thank you dynamicus

user3708642
  • 124
  • 10
Dynamikus
  • 2,861
  • 4
  • 22
  • 20
  • Do you have any flexibility in the incoming format? Or do you need to adhere to that EXACT structure? – Shawn Dec 07 '11 at 14:10

2 Answers2

2

I was curious to see if I could get a custom model binder to work for your scenario. Below is a very crude example that works with your incoming data. Please note it needs much improvement, but does bind successfully:

Given the following view models:

public class OraFormData
{
    public IDictionary<string, Duration> form { get; set; }
}

public class Duration
{
    public string Start { get; set; }
    public string End { get; set; }
}

Using this custom model binder:

public class OraFormDataModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        const string FORMKEY = "form[0][{0}][{1}]";
        const string PREFIX = "form[0][";

        try
        {
            var form = controllerContext.HttpContext.Request.Form;

            var vm = new OraFormData();

            var oraKeys = (from mainKey in form.AllKeys
                           where mainKey.StartsWith(PREFIX)
                           let trimmedKey = mainKey.Replace(PREFIX, String.Empty)
                           select trimmedKey.Substring(0, trimmedKey.IndexOf(']'))).Distinct().ToList();

            vm.form = new Dictionary<string, Duration>(oraKeys.Count);
            foreach (var oraKey in oraKeys)
            {
                vm.form.Add(oraKey, new Duration
                                        {
                                            Start = form[string.Format(FORMKEY, oraKey, "start")],
                                            End = form[string.Format(FORMKEY, oraKey, "end")]
                                        });
            }

            return vm;
        }
        catch
        {
            return null;
        }
    }
}

The following action binds when using the string from your first example:

[HttpPost]
public ActionResult TestPost([ModelBinder(typeof(OraFormDataModelBinder))]OraFormData form)
{
    // added modelbinder here just for example, could move to global.asax
    return Json(...);
}
Shawn
  • 1,871
  • 2
  • 21
  • 36
1

According to the answers in this post, binding to a Dictionary object as you have outlined is not natively supported. However, one of the answers to that same question apparently created a custom ModelBinder to achieve the desired result.

If you have more control over the incoming json data, you could do something like this:

public class OraViewModel
{
    public IList<LineItem> LineItems { get; set; }
}

public class LineItem
{
    public string Name { get; set; }
    public Duration Duration { get; set; }
}

public class Duration
{
    public string Start { get; set; }
    public string End { get; set; }
}

Then from your view you would post like this:

$('#buttonId').click(function () {

    var data = { LineItems: [{ Name: "name 0", Duration: { Start: "start 0", End: "end 0"} }, { Name: "name 1", Duration: { Start: "start 1", End: "end 1"} }, { Name: "name 2", Duration: { Start: "start 2", End: "end 2"} }, { Name: "name 3", Duration: { Start: "start 3", End: "end 3"} }, { Name: "name 4", Duration: { Start: "start 4", End: "end 4"}}] };

    $.ajax({
        url: "/home/testpost2",
        data: JSON.stringify(data),  //*** using JSON2.js to stringify the js object
        type: "POST",
        contentType: 'application/json',   // *** note the content type is set to json
        dataType: 'json',
    });
});

Doing so allowed the following controller action to properly bind the incoming data to my object:

[HttpPost]
public ActionResult TestPost2(OraViewModel data)
{
    return Json("whatever you want the return to be");
}
Community
  • 1
  • 1
Shawn
  • 1,871
  • 2
  • 21
  • 36
  • Sorry, now I got what you meant by flexible format. I am using form2js (http://code.google.com/p/form2js/) to collect the input data. I am not sure wherever I can achieve the suggested json format from form2js, I will have a look. Thank you – Dynamikus Dec 07 '11 at 17:04
  • 1
    Here is another reference if you have not seen it already: http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx – Shawn Dec 07 '11 at 17:45
  • Yep, i have seen it but I cannot produce that kind of html. I am giving up on model bidings. I will try to parse it with Json.net. Thank you again!!! – Dynamikus Dec 07 '11 at 18:11