web-dev-qa-db-ja.com

JSONを介してASP.Net MVC3にオブジェクトの配列を投稿する

JSONを介してオブジェクトの配列をMVC3にPOSTするためのソリューションを探しています。

私が働いているサンプルコード: http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx =

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:

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

ItemViewModel.cs:

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

ListController.cs:

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

このPOSTの結果:

リストがListViewModelに設定されます
XおよびYプロパティが設定されています
基になるItemListプロパティが設定されます
ItemListには1つのアイテムが含まれています。
そのItemListのアイテムは初期化されていません。 Strはnullで、Enabledはfalseです。

別の言い方をすれば、これはMVC3のモデルバインディングから得られるものです。

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

MVC3 JsonValueProviderが複雑なオブジェクトに対して機能していないようです。これを機能させるにはどうすればよいですか?既存のMVC3 JsonValueProviderを変更して修正する必要がありますか?もしそうなら、どうやってそれを取得してMVC3プロジェクトで置き換えるのですか?

関連するStackOverflowの質問は、私はすでに無駄に追求しました:

Asp.net Mvc Ajax Json(post Array) MVC2以前のフォームベースのエンコーディングを使用します-このアプローチは、オブジェクトの配列を含むオブジェクトでは失敗します(JQueryは適切にエンコードに失敗します)。

JSON、JQueryからASP.NET MVCコントローラーへの複雑なオブジェクトの配列の投稿 ハックを使用するコントローラーが代わりにプレーン文字列を受信する場所を回避したい枠組み。

MVC3 RC2 JSONポストバインディングが正しく機能しない コンテンツタイプが設定されていなかった-私のコードで設定されています。

JSONを使用した複雑なオブジェクトの配列、jQueryをASP.NET MVCコントローラーに投稿する方法 この貧しい男は、配列を解析するためだけにJsonFilterを作成する必要がありました。私が避けたい別のハック。

それで、どうすればこれを実現できますか?

65
Chris Moschini

問題は、リストにあるモデルのプロパティがパブリックプロパティで取得/設定されていないことでした。別の言い方をすれば、MVC3の自動JSONバインディングは、取得および設定されたオブジェクトプロパティでのみ機能します。

これはバインドしません:

public string Str;

これはバインドします:

public string Str { get; set; }
30
Chris Moschini

に加えて { get; set; }、これらはJSONバインディングサポートの条件の一部です。

  1. これはASP.NET MVC 3の新機能です(「 JavaScript and AJAX Improvements 」を参照)。
  2. JSONオブジェクトの文字列(「X」、「Y」、「Str」、「有効」)は、ViewModelオブジェクトのプロパティと一致する必要があります。
  3. ViewModelオブジェクトのプロパティには{ get; set; } 方法。
  4. リクエストでコンテンツタイプを「application/json」として指定する必要があります。
  5. それでも機能しない場合は、JSON文字列をチェックして、有効であることを確認してください。

詳しくは my post をご覧ください。

お役に立てば幸いです!

43
stack247

それは奇妙です。あなたの行動を再現できません。これが私のセットアップです(ASP.NET MVC 3 RTM):

モデル:

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; }
}

コントローラ:

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

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

表示:

@{
    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>

このアラートの実行"hi"およびSaveアクション内では、すべてが正しく初期化されます。

また、記録のために機能しないのは辞書です。 チケットを開いた 問題について。

29
Darin Dimitrov

同様の問題があり、複雑なオブジェクトの場合、数値が欠落していることがわかりました。彼らはゼロとして入っていた。つまり.

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

プロパティがName=JohnおよびAge=0として設定されているPersonオブジェクトとしてMVCコントローラーによって受信されていました。

次に、JavascriptのAge値を文字列にしました。つまり、.

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

そして、これはうまくいきました...

3
Dev

その理由は、MVCバインダーが一種の吸い込みだからです。ただし、すべてのJSON値が文字列として渡される場合、それらは非常にうまく機能します。

JSでこれを行うと

var myObject = {thisNumber:1.6};

myObject.thisNumber=myObject.thisNumber-.6;

1.0ではなく1に評価されます。

そのため、サーバーに送信すると、その名前のフロートにバインドしようとしますが、1.0ではなく1として送信されたため、検出されません。 MSエンジニアがこれに対するデフォルトのソリューションを思い付かなかったことは非常に不格好でクレイジーです。バインディングがすべてのものを見つけるのに十分スマートであるすべてをストリング化すると、私は見つけます。

したがって、データを送信する前に、すべての値を文字列に変換する文字列化器を介してデータを実行します。

0
Chris Stephens

これまでの回答はすべて、同様の問題の解決策を示してくれました。私はPOST x-www-form-urlencoding の代わりに application/json(contentTypeパラメーターがない場合のデフォルトオプション)__RequestVerificationTokenおよび同時に配列にあるオブジェクトプロパティが値をバインドしないときに問題に直面しました。この問題を解決する方法は、MVCモデルバインダーの内部作業を理解することです。

したがって、基本的に検証トークンを提供する必要がある場合、検証属性で制限されます。また、送信するJSONオブジェクトの一部としてではなく、パラメーターとしてトークンを提供する必要があります。 ValidateAntiForgeryTokenを使用しない場合は、JSON.stringifyを使用できます。しかし、もしそうなら、トークンを渡すことができません。

ContentTypex-www-form-urlencodingと私は、複雑なオブジェクトの配列が次のようなものにシリアル化されたことに気付きました:klo[0][Count]=233&klo[0][Blobs]=94。この配列は最初はルートオブジェクトの一部でしたが、あるモデルを考えてみましょう。そのように見えた:model.klo = [{ Count: 233, Blobs: 94}, ...]

バックエンド側では、このkloプロパティは、送信したものと同じ要素数でMVCバインダーによって作成されていました。しかし、これらの要素自体は、プロパティの値を取得しませんでした。

[〜#〜] solution [〜#〜]

これに対処するために、クライアント側でモデルオブジェクトから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;
    }

この関数は、複雑なオブジェクトの配列をMVCバインダーによって認識される形式に変換します。フォームはklo[0].Count=233&klo[0].Blobs=94

0
kokosda