通过JSON将对象数组发布到ASP。净MVC3

时间:2022-11-28 18:32:57

I'm looking for a solution to POSTing an array of objects to MVC3 via JSON.

我正在寻找通过JSON向MVC3发布对象数组的解决方案。

Example code I'm working off of: http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx

我正在处理的示例代码:http://weblogs.asp.net/scottgu/archive0/07/27 / -asp-net-mvc-3-preview-1.aspx

JS:

JS:

var data = { ItemList: [ {Str: 'hi', Enabled: true} ], X: 1, Y: 2 };

$.ajax({
    url: '/list/save',
    data: JSON.stringify(data),
    success: success,
    error: error,
    type: 'POST',
    contentType: 'application/json, charset=utf-8',
    dataType: 'json'
});

ListViewModel.cs:

ListViewModel.cs:

public class ListViewModel
{
    public List<ItemViewModel> ItemList { get; set; }
    public float X { get; set; }
    public float Y { get; set; }
}

ItemViewModel.cs:

ItemViewModel.cs:

public class ItemViewModel
{
    public string Str;   // originally posted with: { get; set; }
    public bool Enabled; // originally posted with: { get; set; }
}

ListController.cs:

ListController.cs:

public ActionResult Save(ListViewModel list)
{
    // Do something
}

The result of this POST:

这篇文章的结果:

list is set, to a ListViewModel
Its X and Y properties are set
The underlying ItemList property is set
The ItemList contains one item, as it should
The item in that ItemList is uninitialized. Str is null and Enabled is false.

list被设置,对于ListViewModel,它的X和Y属性被设置,下面的ItemList属性被设置,ItemList包含一个条目,如果ItemList中的条目没有初始化。Str为null, Enabled为false。

Put another way, this is what I get from MVC3's model binding:

换句话说,这是我从MVC3的模型绑定中得到的:

list.X == 1
list.Y == 2
list.ItemList != null
list.ItemList.Count == 1
list.ItemList[0] != null
list.ItemList[0].Str == null

It would appear the MVC3 JsonValueProvider is not working for complex objects. How do I get this to work? Do I need to modify the existing MVC3 JsonValueProvider and fix it? If so, how do I get at it and replace it in an MVC3 project?

看起来MVC3 JsonValueProvider并不适合复杂对象。我怎么让它工作呢?我是否需要修改现有的MVC3 JsonValueProvider并修复它?如果是这样,我如何获得它并在MVC3项目中替换它?

Related * questions I've already pursued to no avail:

相关的*问题,我已经没有使用过:

Asp.net Mvc Ajax Json (post Array) Uses MVC2 and older form-based encoding - that approach fails with an object that contains an array of objects (JQuery fails to encode it properly).

net Mvc Ajax Json (post Array)使用MVC2和旧的基于表单的编码——这种方法在包含对象数组的对象中失败(JQuery未能正确地对其进行编码)。

Post an array of complex objects with JSON, JQuery to ASP.NET MVC Controller Uses a hack I'd like to avoid where the Controller instead receives a plain string which it then manually deserializes itself, rather than leveraging the framework.

用JSON、JQuery和ASP发布一个复杂对象数组。NET MVC控制器使用了一种我希望避免的方法,即控制器接收到一个纯字符串,然后手动反序列化它自己,而不是利用框架。

MVC3 RC2 JSON Post Binding not working correctly Didn't have his content-type set - it's set in my code.

MVC3 RC2 JSON Post绑定没有正确工作,没有设置他的内容类型-它是在我的代码中设置的。

How to post an array of complex objects with JSON, jQuery to ASP.NET MVC Controller? This poor guy had to write a JsonFilter just to parse an array. Another hack I'd prefer to avoid.

如何使用JSON、jQuery和ASP发布复杂对象数组。净MVC控制器吗?这个可怜的家伙不得不编写一个JsonFilter来解析数组。另一个我宁愿避免的黑客攻击。

So, how do I make this happen?

那么,我该怎么做呢?

6 个解决方案

#1


43  

In addition to { get; set; }, these are some of the conditions for JSON Binding Support:

除了{get;设置;},这些是JSON绑定支持的一些条件:

  1. This is new feature in ASP.NET MVC 3 (See “JavaScript and AJAX Improvements“).
  2. 这是ASP的新功能。NET MVC 3(参见“JavaScript和AJAX改进”)。
  3. The JSON object’s strings (‘X’, ‘Y’, ‘Str’, and ‘Enabled’) must match ViewModel object’s properties.
  4. JSON对象的字符串(' X '、' Y '、' Str '和' Enabled ')必须匹配ViewModel对象的属性。
  5. ViewModel object’s properties must have { get; set; } method.
  6. ViewModel对象的属性必须具有{get;设置;}的方法。
  7. Must specify Content Type as “application/json” in the request.
  8. 必须在请求中指定内容类型为“application/json”。
  9. If it's still not working, check the JSON string to make sure it's valid one.
  10. 如果它仍然不能工作,请检查JSON字符串以确保它是有效的。

Read more at my post.

在我的帖子里多读一些。

Hope that helps!

希望会有帮助!

#2


30  

The problem was that the properties in the models that were in the List did not have get/set on their public properties. Put another way, MVC3's automatic JSON binding only works on object properties that have get and set.

问题是,列表中的模型中的属性没有得到/设置它们的公共属性。换句话说,MVC3的自动JSON绑定只适用于具有get和set的对象属性。

This will not bind:

这不会绑定:

public string Str;

This will bind:

这将绑定:

public string Str { get; set; }

#3


28  

That's strange. I am unable to reproduce your behavior. Here's my setup (ASP.NET MVC 3 RTM):

这是奇怪的。我无法再现你的行为。这是我的设置(ASP。NET MVC 3 RTM):

Model:

模型:

public class ItemViewModel
{
    public string Str { get; set; }
    public bool Enabled { get; set; }
}

public class ListViewModel
{
    public List<ItemViewModel> ItemList { get; set; }
    public float X { get; set; }
    public float Y { get; set; }
}

Controller:

控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Save(ListViewModel list)
    {
        return Json(list);
    }
}

View:

观点:

@{
    ViewBag.Title = "Home Page";
}

<script type="text/javascript">
    $(function () {
        var data = { ItemList: [{ Str: 'hi', Enabled: true}], X: 1, Y: 2 };

        $.ajax({
            url: '@Url.Action("save", "home")',
            data: JSON.stringify(data),
            type: 'POST',
            contentType: 'application/json',
            dataType: 'json',
            success: function (result) {
                alert(result.ItemList[0].Str);
            }
        });
    });
</script>

Running this alerts "hi" and inside the Save action everything is correctly initialized.

运行此警报“hi”并在Save操作中正确初始化所有内容。

And just for the record what doesn't work are Dictionaries. I've opened a ticket about the issue.

仅供参考的是字典。我已经开了一张有关这个问题的票。

#4


3  

I had a similar issue, and found that for a complex object, the numeric values were getting missed. They were coming in as zeros. i.e.

我遇到了类似的问题,发现对于一个复杂的对象,数值被忽略了。它们都是0。即。

    var person = {
        Name: "john",
        Age: 9
    }

was being received by MVC controller as a Person object where the properties were being populated as Name=John and Age=0.

被MVC控制器接收为Person对象,其中属性被填充为Name=John, Age=0。

I then made the Age value in Javascript to be string... i.e.

然后我将Javascript中的年龄值设为string…即。

    var person = {
        Name: "john",
        Age: "9"
    }

And this came through just fine...

这一切都很顺利……

#5


0  

Its because the MVC binders kind of suck. However, they do work pretty well if all JSON values come over as a string.

因为MVC绑定器很糟糕。但是,如果所有的JSON值都作为字符串出现,那么它们的工作就会很好。

In JS if you do this

用JS

var myObject = {thisNumber:1.6};

myObject.thisNumber=myObject.thisNumber-.6;

It will evaluate to 1 not to 1.0

它的值是1,而不是1。0。

So when you sent it over to the server it will try to bind to a float of that name and it will not find it since it came over as 1 instead of 1.0. Its very lame and crazy that MS engineers did not come up with a default solution to this. I find if you string everything the bindings are smart enough to find things.

当你把它发送到服务器时它会尝试绑定到那个名字的浮点数它不会找到它因为它是1而不是1。MS的工程师们没有为这个问题提供一个默认的解决方案,这是非常蹩脚和疯狂的。我发现如果你把所有的东西都串起来绑定就足够聪明了。

So before sending the data over run it though a stringifier that will also convert all values to strings.

所以在发送数据之前,通过一个stringifier来运行它,这个stringifier也会将所有的值转换为字符串。

#6


0  

All previous answers were great to point me to solution of the similar problem. I had to POST x-www-form-urlencoding instead of application/json (default option if contentType parameter is missing) to be able to pass __RequestVerificationToken and simultaneously faced with problem when object properties being in the array do not bind their values. The way to solve the issue is to understand internal work of MVC model binder.

之前所有的答案都很好地为我指出了解决类似问题的方法。我必须发布x-www-form-urlencoding而不是应用程序/json(如果缺少contentType参数,默认选项),以便能够传递__RequestVerificationToken,同时在数组中的对象属性不绑定它们的值时也会遇到问题。解决这一问题的方法是了解MVC模型绑定器的内部工作。

So, basically when you need to supply verification token you are restricted with validation attribute. And you must provide the token as the parameter not as a part of the JSON-object you are sending. If you would not use ValidateAntiForgeryToken, you could get along with JSON.stringify. But if you would, you could not pass the token.

因此,基本上当您需要提供验证令牌时,您将受到验证属性的限制。而且您必须提供令牌作为参数,而不是作为要发送的json对象的一部分。如果不使用ValidateAntiForgeryToken,可以使用JSON.stringify。但如果你愿意,你就不能传递令牌。

I sniffed traffic to backend when ContentType was x-www-form-urlencoding and I remarked that my array of complex objects was serialized to something like that: klo[0][Count]=233&klo[0][Blobs]=94. This array initially was a part of root object, let's say some model. It looked like that: model.klo = [{ Count: 233, Blobs: 94}, ...].

当ContentType是x-www-form-urlencoding时,我嗅探了一下后端通信,发现我的复杂对象数组被序列化成这样:klo[0][Count]=233&klo[0][Blobs]=94。这个数组最初是根对象的一部分,假设是某个模型。它看起来是这样的:模型。klo = [{Count: 233, Blobs: 94},…]

At the backend side this klo property was creating by MVC binder with the same elements count that I sent. But these elements itself did not obtain values for their properties.

在后端,klo属性是由MVC binder用我发送的元素计数创建的。但是这些元素本身并没有获得它们的属性值。

SOLUTION

解决方案

To deal with this I excluded klo property from the model object at the client side. In the ajax function I wrote this code:

为了处理这个问题,我将klo属性从客户端模型对象中排除。在ajax函数中,我编写了以下代码:

data: $.param(model) + "&" + arrayOfObjectsToFormEncoding("klo", [{ Count: 233, Blobs: 94}, ...])
....

    function arrayOfObjectsToFormEncoding (modelPropertyName, arrayOfObjects) {
        var result = "";
        if (arrayOfObjects && typeof arrayOfObjects == "object") {
            for (var i = 0; i < arrayOfObjects.length; i++) {
                var obj = arrayOfObjects[i];
                if (obj) {
                    for (var p in obj) {
                        if (obj.hasOwnProperty(p)) {
                            result += encodeURIComponent(modelPropertyName + "[" + i + "]." + p) + "=" + encodeURIComponent(obj[p]) + "&";
                        }
                    }
                }
            }
        }

        if (result[result.length - 1] == "&") {
            result = result.substr(0, result.length - 1);
        }

        return result;
    }

The function transforms array of complex object into form that is recognized by MVC-binder. The form is klo[0].Count=233&klo[0].Blobs=94.

该函数将复杂对象的数组转换为MVC-binder识别的形式。表单klo[0].Count = 233 &klo[0].Blobs = 94。

#1


43  

In addition to { get; set; }, these are some of the conditions for JSON Binding Support:

除了{get;设置;},这些是JSON绑定支持的一些条件:

  1. This is new feature in ASP.NET MVC 3 (See “JavaScript and AJAX Improvements“).
  2. 这是ASP的新功能。NET MVC 3(参见“JavaScript和AJAX改进”)。
  3. The JSON object’s strings (‘X’, ‘Y’, ‘Str’, and ‘Enabled’) must match ViewModel object’s properties.
  4. JSON对象的字符串(' X '、' Y '、' Str '和' Enabled ')必须匹配ViewModel对象的属性。
  5. ViewModel object’s properties must have { get; set; } method.
  6. ViewModel对象的属性必须具有{get;设置;}的方法。
  7. Must specify Content Type as “application/json” in the request.
  8. 必须在请求中指定内容类型为“application/json”。
  9. If it's still not working, check the JSON string to make sure it's valid one.
  10. 如果它仍然不能工作,请检查JSON字符串以确保它是有效的。

Read more at my post.

在我的帖子里多读一些。

Hope that helps!

希望会有帮助!

#2


30  

The problem was that the properties in the models that were in the List did not have get/set on their public properties. Put another way, MVC3's automatic JSON binding only works on object properties that have get and set.

问题是,列表中的模型中的属性没有得到/设置它们的公共属性。换句话说,MVC3的自动JSON绑定只适用于具有get和set的对象属性。

This will not bind:

这不会绑定:

public string Str;

This will bind:

这将绑定:

public string Str { get; set; }

#3


28  

That's strange. I am unable to reproduce your behavior. Here's my setup (ASP.NET MVC 3 RTM):

这是奇怪的。我无法再现你的行为。这是我的设置(ASP。NET MVC 3 RTM):

Model:

模型:

public class ItemViewModel
{
    public string Str { get; set; }
    public bool Enabled { get; set; }
}

public class ListViewModel
{
    public List<ItemViewModel> ItemList { get; set; }
    public float X { get; set; }
    public float Y { get; set; }
}

Controller:

控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Save(ListViewModel list)
    {
        return Json(list);
    }
}

View:

观点:

@{
    ViewBag.Title = "Home Page";
}

<script type="text/javascript">
    $(function () {
        var data = { ItemList: [{ Str: 'hi', Enabled: true}], X: 1, Y: 2 };

        $.ajax({
            url: '@Url.Action("save", "home")',
            data: JSON.stringify(data),
            type: 'POST',
            contentType: 'application/json',
            dataType: 'json',
            success: function (result) {
                alert(result.ItemList[0].Str);
            }
        });
    });
</script>

Running this alerts "hi" and inside the Save action everything is correctly initialized.

运行此警报“hi”并在Save操作中正确初始化所有内容。

And just for the record what doesn't work are Dictionaries. I've opened a ticket about the issue.

仅供参考的是字典。我已经开了一张有关这个问题的票。

#4


3  

I had a similar issue, and found that for a complex object, the numeric values were getting missed. They were coming in as zeros. i.e.

我遇到了类似的问题,发现对于一个复杂的对象,数值被忽略了。它们都是0。即。

    var person = {
        Name: "john",
        Age: 9
    }

was being received by MVC controller as a Person object where the properties were being populated as Name=John and Age=0.

被MVC控制器接收为Person对象,其中属性被填充为Name=John, Age=0。

I then made the Age value in Javascript to be string... i.e.

然后我将Javascript中的年龄值设为string…即。

    var person = {
        Name: "john",
        Age: "9"
    }

And this came through just fine...

这一切都很顺利……

#5


0  

Its because the MVC binders kind of suck. However, they do work pretty well if all JSON values come over as a string.

因为MVC绑定器很糟糕。但是,如果所有的JSON值都作为字符串出现,那么它们的工作就会很好。

In JS if you do this

用JS

var myObject = {thisNumber:1.6};

myObject.thisNumber=myObject.thisNumber-.6;

It will evaluate to 1 not to 1.0

它的值是1,而不是1。0。

So when you sent it over to the server it will try to bind to a float of that name and it will not find it since it came over as 1 instead of 1.0. Its very lame and crazy that MS engineers did not come up with a default solution to this. I find if you string everything the bindings are smart enough to find things.

当你把它发送到服务器时它会尝试绑定到那个名字的浮点数它不会找到它因为它是1而不是1。MS的工程师们没有为这个问题提供一个默认的解决方案,这是非常蹩脚和疯狂的。我发现如果你把所有的东西都串起来绑定就足够聪明了。

So before sending the data over run it though a stringifier that will also convert all values to strings.

所以在发送数据之前,通过一个stringifier来运行它,这个stringifier也会将所有的值转换为字符串。

#6


0  

All previous answers were great to point me to solution of the similar problem. I had to POST x-www-form-urlencoding instead of application/json (default option if contentType parameter is missing) to be able to pass __RequestVerificationToken and simultaneously faced with problem when object properties being in the array do not bind their values. The way to solve the issue is to understand internal work of MVC model binder.

之前所有的答案都很好地为我指出了解决类似问题的方法。我必须发布x-www-form-urlencoding而不是应用程序/json(如果缺少contentType参数,默认选项),以便能够传递__RequestVerificationToken,同时在数组中的对象属性不绑定它们的值时也会遇到问题。解决这一问题的方法是了解MVC模型绑定器的内部工作。

So, basically when you need to supply verification token you are restricted with validation attribute. And you must provide the token as the parameter not as a part of the JSON-object you are sending. If you would not use ValidateAntiForgeryToken, you could get along with JSON.stringify. But if you would, you could not pass the token.

因此,基本上当您需要提供验证令牌时,您将受到验证属性的限制。而且您必须提供令牌作为参数,而不是作为要发送的json对象的一部分。如果不使用ValidateAntiForgeryToken,可以使用JSON.stringify。但如果你愿意,你就不能传递令牌。

I sniffed traffic to backend when ContentType was x-www-form-urlencoding and I remarked that my array of complex objects was serialized to something like that: klo[0][Count]=233&klo[0][Blobs]=94. This array initially was a part of root object, let's say some model. It looked like that: model.klo = [{ Count: 233, Blobs: 94}, ...].

当ContentType是x-www-form-urlencoding时,我嗅探了一下后端通信,发现我的复杂对象数组被序列化成这样:klo[0][Count]=233&klo[0][Blobs]=94。这个数组最初是根对象的一部分,假设是某个模型。它看起来是这样的:模型。klo = [{Count: 233, Blobs: 94},…]

At the backend side this klo property was creating by MVC binder with the same elements count that I sent. But these elements itself did not obtain values for their properties.

在后端,klo属性是由MVC binder用我发送的元素计数创建的。但是这些元素本身并没有获得它们的属性值。

SOLUTION

解决方案

To deal with this I excluded klo property from the model object at the client side. In the ajax function I wrote this code:

为了处理这个问题,我将klo属性从客户端模型对象中排除。在ajax函数中,我编写了以下代码:

data: $.param(model) + "&" + arrayOfObjectsToFormEncoding("klo", [{ Count: 233, Blobs: 94}, ...])
....

    function arrayOfObjectsToFormEncoding (modelPropertyName, arrayOfObjects) {
        var result = "";
        if (arrayOfObjects && typeof arrayOfObjects == "object") {
            for (var i = 0; i < arrayOfObjects.length; i++) {
                var obj = arrayOfObjects[i];
                if (obj) {
                    for (var p in obj) {
                        if (obj.hasOwnProperty(p)) {
                            result += encodeURIComponent(modelPropertyName + "[" + i + "]." + p) + "=" + encodeURIComponent(obj[p]) + "&";
                        }
                    }
                }
            }
        }

        if (result[result.length - 1] == "&") {
            result = result.substr(0, result.length - 1);
        }

        return result;
    }

The function transforms array of complex object into form that is recognized by MVC-binder. The form is klo[0].Count=233&klo[0].Blobs=94.

该函数将复杂对象的数组转换为MVC-binder识别的形式。表单klo[0].Count = 233 &klo[0].Blobs = 94。