13

We know that MVC returns DateTime for JsonResult in this format: /Date(1240718400000)/, and we know how to parse it in JS.

However, It seems that MVC doesn't accept DateTime parameter being sent in this way. For example, I have the following Action.

[HttpGet]
public ViewResult Detail(BookDetail details) { //... }

The BookDetail class contains a DateTime field named CreateDate, and I passed a JSON object from JS in this format:

{"CreateDate": "/Date(1319144453250)/"}

CreateDate is recognized as null.

If I passed the JSON in this way, it works as expected:

{"CreateDate": "2011-10-10"}

The problem is that I cannot change client side code in an easy way, have to stick to /Date(1319144453250)/ this format. I have to make changes in server side.

How to solve this problem? Is that anything related to ModelBinder?

Thanks so much in advance!

1

3 Answers 3

7

The problem, as you suspected, is a model binding issue.

To work around it, create a custom type, and let's call it JsonDateTime. Because DateTime is a struct, you cannot inherit from it, so create the following class:

public class JsonDateTime
{
    public JsonDateTime(DateTime dateTime)
    {
        _dateTime = dateTime;
    }

    private DateTime _dateTime;

    public DateTime Value
    {
        get { return _dateTime; }
        set { _dateTime = value; }
    }
}

Change CreateDate to this type. Next, we need a custom model binder, like so:

public class JsonDateTimeModelBinder : IModelBinder  
{ 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ToString(); 
        return new DateTime(Int64.Parse(
            value.Substring(6).Replace(")/",String.Empty))); // "borrowed" from skolima's answer
    }
}

Then, in Global.asax.cs, in Application_Start, register your custom ModelBinder:

ModelBinders.Binders.Add(typeof(JsonDateTime), new JsonDateTimeModelBinder());
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks counsellorben (suppose this is your name:), i'll try it out. Can you tell me more about the ModelBinder, and maybe point out the relevant area in MVC's source code?
You should look at the DefaultModelBinder. Look at DefaultModelBinder.cs in the System.Web.Mvc project, in the MVC folder.
Why is the wrapper object JsonDateTime being created? You mention '...because DateTime is a struct, you cannot inherit from it', but I see no inheritance being used here.
in MVC 4 I need to use ...bindingContext.ValueProvider.GetValue(bindingContext.ModelName).RawValue.ToString() ...
2

In your model, use this to parse the date:

// property
String CreateDate;
DateTime CreateDateAsDate;

// drop prefix, drop suffix, parse as long and read as ticks
CreateDateAsDate date = new DateTime(Int64.Parse(
    CreateDate.Substring(6).Replace(")/",String.Empty)));

3 Comments

CreateDate is recognized as null, so this doesn't work. (Unless I define CreateDate as String, but I really don't want to do that...)
You'll have two properties, CreateDate as String and CreateDateAsDate with the actual parsed value. I'm afraid it won't work otherwise.
Seems like this is the only way...I'll wait a little bit longer for more replies ;-)
2

I think using custom Model Binder will do the trick. The below model binder class will work on both cases. It will parse all dot net recognizable date string as well as JSON formatted date string. No need to change any existing code.

public class DateTimeModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var model = base.BindModel(controllerContext, bindingContext);
        if (model == null && (bindingContext.ModelType == typeof(DateTime) || bindingContext.ModelType == typeof(DateTime?)))
        {
            var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (value == null || String.IsNullOrEmpty(value.AttemptedValue))
                model = (bindingContext.ModelType == typeof(DateTime?)) ? null : (object)DateTime.MinValue;
            else if (Regex.IsMatch(value.AttemptedValue, @"\/Date\(\d+\)\/"))
                model = new DateTime(1970, 1, 1).AddMilliseconds(Int64.Parse(value.AttemptedValue.Substring(6).Replace(")/", String.Empty))).ToLocalTime();
            //else //Any other format
        }
        return model;
    }
}

Configure Model Binder in Application_Start of Global.asax

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        //Your Existing Code....

        ModelBinders.Binders.Add(typeof(DateTime), new DateTimeModelBinder());
        ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeModelBinder());
    }
}

Vote If it helps

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.