ASP.NET MVC如何将JSON对象从视图传递到控制器作为参数

54

我有一个复杂的JSON对象,可以轻松地将其发送到视图中(如下所示),但是当通过AJAX调用将其传递回控制器时,我无法弄清楚如何将此数据序列化回.NET对象。各个部分的详细信息如下。

   var ObjectA = {
        "Name": 1,
        "Starting": new Date(1221644506800),

        "Timeline": [
            {
                "StartTime": new Date(1221644506800),
                "GoesFor": 200

            }
            ,
            {
                "StartTime": new Date(1221644506800),
                "GoesFor": 100

            }

        ]
    };

我不确定如何将这个对象传递给控制器方法,我有以下方法,其中Timelines对象使用Properties镜像上面的JS对象。

public JsonResult Save(Timelines person)

我使用的 jQuery 版本是:

        var encoded = $.toJSON(SessionSchedule);

        $.ajax({
            url: "/Timeline/Save",
            type: "POST",
            dataType: 'json',
            data: encoded,
            contentType: "application/json; charset=utf-8",
            beforeSend: function() { $("#saveStatus").html("Saving").show(); },
            success: function(result) {
                alert(result.Result);
                $("#saveStatus").html(result.Result).show();
            }
        });

我看到了这个类似的问题,但并不完全相同,因为我没有使用表单来操作数据。 如何使用JSON将复杂类型传递给ASP.NET MVC控制器

我也看到了使用“JsonFilter”手动反序列化JSON的参考资料,但想知道是否有一种通过ASP.NET MVC本地实现的方法。或者在这种情况下传递数据的最佳实践是什么?

6个回答

54

编辑:

随着MVC 3的推出,这个方法应该不再需要了,因为它将会被自动处理 - http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx


你可以使用这个 ObjectFilter:

    public class ObjectFilter : ActionFilterAttribute {

    public string Param { get; set; }
    public Type RootType { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext) {
        if ((filterContext.HttpContext.Request.ContentType ?? string.Empty).Contains("application/json")) {
            object o =
            new DataContractJsonSerializer(RootType).ReadObject(filterContext.HttpContext.Request.InputStream);
            filterContext.ActionParameters[Param] = o;
        }

    }
}

然后,您可以像这样将其应用于控制器方法:

    [ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))]
    public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... }

基本上,如果帖子的内容类型是“application/json”,这将会启动并将值映射到您指定类型的对象中。


1
很好的回答,不确定为什么这不是被采纳的答案,但肯定对我有帮助... - JonoW
3
我使用这种方法来实现相同的事情,但是由于某些原因,在ReadObject方法上出现了异常:“期望来自命名空间''的元素'root'.. 遇到名称为''、命名空间为''的'None'。” 有任何想法为什么会这样? - Dan Appleyard
我该如何添加更多不是 JSON 的操作参数? JsonResult blah(string s,Person p)。我能将 JSON 对象绑定到 Person p,但我不知道如何与 JSON 对象一起传递字符串 s。 - Chev
算了,我自己解决了。我添加了我的发现作为后续回答。感谢您的帮助 DaRKoN_! - Chev

13
您说“我没有使用表单来操作数据。” 但是您正在进行POST。 因此,即使它是空的,您实际上也在使用表单。 $.ajax的dataType告诉jQuery服务器将返回什么类型,而不是您正在传递什么。 POST只能传递表单。 jQuery 将数据转换为键/值对并将其作为查询字符串传递。从文档中可以看出: 要发送到服务器的数据。如果尚未成为字符串,则会将其转换为查询字符串。对于GET请求,它将附加到URL上。请参阅processData选项以防止此自动处理。对象必须是键/值对。如果值是数组,则jQuery使用相同键序列化多个值,例如{foo:[“bar1”,“bar2”]}变为'& foo = bar1& foo = bar2'。 因此: 1. 您没有向服务器传递JSON。 您正在向jQuery传递JSON。 2. 模型绑定与任何其他情况下发生的方式相同。

4
不,他不是在使用表单,无论实际情况如何。他正在使用POST动词发送HTTP请求。这个请求可能会使用表单,也可能是RESTful服务调用等等。 - Cornelius

11

使用简单的jQuery插件实现异曲同工

虽然这个问题的答案已经过期,但我仍然想要分享一个不错的解决方案。我有一段时间前写的代码可以让你将复杂的JSON发送给Asp.net MVC控制器动作,使它们绑定到任何强类型参数上。

这款插件同样也支持处理日期,将其转换为相应的DateTime类型。

你可以在我的博客文章中找到所有细节,我在那篇文章中解释了这个问题并提供了必要的代码。

你只需要在客户端使用这个插件即可,下面是一个Ajax请求的例子:

$.ajax({
    type: "POST",
    url: "SomeURL",
    data: $.toDictionary(yourComplexJSONobject),
    success: function() { ... },
    error: function() { ... }
});

但这只是整个问题的一部分。现在我们能够将复杂的JSON发送回服务器,但由于它将被绑定到可能具有属性验证属性的复杂类型上,因此在这一点上可能会出现问题。我也 解决了这个问题。我的解决方案利用了jQuery Ajax功能,其中结果可以是成功或错误的(就像上面的代码所示)。因此,当验证失败时,error函数将被调用,就像它应该被调用的那样。


4

还有一个可以使用的类是JavaScriptSerializer。使用它可以将json反序列化为.NET对象。有一个通用的Deserialize<T>方法,但需要.NET对象与javascript对象具有相似的签名。此外还有一个DeserializeObject方法,它只创建一个普通的object对象。然后您可以使用反射来获取所需的属性。

如果您的控制器接受一个FormCollection,并且您没有将任何其他内容添加到data中,则json应该在form[0]中。

public ActionResult Save(FormCollection forms) {
  string json = forms[0];
  // do your thing here.
}

2
这篇答案是对DaRKoN_使用的对象过滤器进行跟进的。
[ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))]
    public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... }

我曾经遇到一个问题,就是如何将多个参数发送给一个操作方法,并且其中一个是json对象,另一个是普通字符串。我刚开始接触MVC,忘记了我已经在非ajax视图中解决过这个问题。

如果我需要在一个视图中使用两个不同的对象,我会创建一个ViewModel类。比如说我需要person对象和address对象,我会按照以下步骤进行:

public class SomeViewModel()
{
     public Person Person { get; set; }
     public Address Address { get; set; }
}

然后我会将视图绑定到SomeViewModel上。你也可以用JSON做同样的事情。

[ObjectFilter(Param = "jsonViewModel", RootType = typeof(JsonViewModel))] // Don't forget to add the object filter class in DaRKoN_'s answer.
public JsonResult doJsonStuff(JsonViewModel jsonViewModel)
{
     Person p = jsonViewModel.Person;
     Address a = jsonViewModel.Address;
     // Do stuff
     jsonViewModel.Person = p;
     jsonViewModel.Address = a;
     return Json(jsonViewModel);
}

那么在视图中,你可以使用 JQuery 中的一个简单调用,如下所示:

var json = { 
    Person: { Name: "John Doe", Sex: "Male", Age: 23 }, 
    Address: { Street: "123 fk st.", City: "Redmond", State: "Washington" }
};

$.ajax({
     url: 'home/doJsonStuff',
     type: 'POST',
     contentType: 'application/json',
     dataType: 'json',
     data: JSON.stringify(json), //You'll need to reference json2.js
     success: function (response)
     {
          var person = response.Person;
          var address = response.Address;
     }
});

这实际上与将JSON发送到服务器的细节无关,但它是一个很好的例子,说明复杂的JSON对象可以成为总体JSON对象(超级复杂?)的一部分,并以此方式来回传递。 - Chev
我正在学习MVC。所以你能告诉我什么是JsonViewModel吗?它是在MVC3中的任何内置类吗? - Thomas
@Thomas 不是的。这只是我创建的一个普通对象。我称之为JsonViewModel,因为它旨在保存刚刚反序列化或即将序列化的JSON数据。 - Chev

1

回应Dan上面的评论:

我正在使用这种方法来实现同样的事情,但由于某些原因,在ReadObject方法上出现异常:“期望来自命名空间''的元素'root'。遇到名称为'',命名空间为''的“无”。” 有什么想法吗?- Dan Appleyard Apr 6 '10 at 17:57

我也遇到了同样的问题(MVC 3 build 3.0.11209.0),下面的帖子为我解决了这个问题。基本上,json序列化器试图读取一个不在开头的流,所以将流重新定位到0就“修复”了它...

http://nali.org/asp-net-mvc-expecting-element-root-from-namespace-encountered-none-with-name-namespace/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接