25

さまざまなプロパティを含む整数とオブジェクトを受け取るコントローラー アクションがあり、そのうちの 1 つはオブジェクトの一般的なリストです。データが入力されたリストを使用して JSON をアクションに投稿すると、すべてが正しくマップされ、投稿したオブジェクトを含むリストが取得されます。ただし、配列が空の場合、MVC アクションはプロパティを空のリストではなく null にバインドします。この場合の空の配列はコレクションに何もないことを意味し、null はデータベースをチェックして以前に何かがあるかどうかを確認する必要があることを意味するため、空の配列を null ではなく空の配列にマップする必要があります。コレクションに保存されていますが、適切にマップするために何を変更する必要があるかわかりません。Json.Net を使用して、オブジェクトを返すためのオブジェクトのシリアル化を行っていますが、そうではないと思います。

渡されるオブジェクト:

public class ObjectInList
{
    public decimal Value1 { get; set; }
    public decimal Value2 { get; set; }
}

public class Criteria
{
    public decimal? ANullableNumber { get; set; }
    public IList<ObjectInList> ObjectsList { get; set; }
}

Json リクエスト: "{\"id\":137,\"criteria\":{\"ObjectsList\":[]}}"

コントローラーのアクション:

public ActionResult ProcessCriteria(int id, Criteria criteria)
{
    return Json(_service.ProcessCriteria(id, criteria));
}

基準オブジェクトで空のリストではなく null を取得しているのは、コントローラー アクションです。他のプロパティに null を送信するかどうかに関係なく発生します。オブジェクトが IEnumerable ではなく IList であることが原因かどうかわかりませんか? (サービス呼び出しをラップする Json メソッドは、Json.Net を使用して json の結果を返し、応答をシリアル化するラッパーです。null は、戻り値ではなく、受信した基準オブジェクトにあります。)

私が見逃しているのはかなり単純なものだと思いますが、何を解決することはできません。どんな助けも大歓迎です。

4

6 に答える 6

12

わかりました、解決策を見つけようとしてほぼ5時間この問題に直面していましたが、MVCソースコードを調べていることに気づきました。これは、 System.Web.Mvc.ValueProviderResult の 173 行目の Mvc ソース コードに問題があることがわかりました。

        else if (valueAsArray != null)
        {
            // case 3: destination type is single element but source is array, so                     extract first element + convert
            if (valueAsArray.Length > 0)
            {
                value = valueAsArray.GetValue(0);
                return ConvertSimpleType(culture, value, destinationType);
            }
            else
            {
                // case 3(a): source is empty array, so can't perform conversion
                return null;
            }
        }

ソースが空の配列である場合にわかるように、null が返されます。

だから私はそれを回避する方法を見つけなければなりません.そして、古き良き時代にデシリアライゼーションを行っていた方法を思い出します.これは、あなたが望むものを得る方法です:

    public ActionResult ProcessCriteria(int id, Criteria criteria)
    {
        var ser = new System.Web.Script.Serialization.JavaScriptSerializer();
        StreamReader reader = new StreamReader(System.Web.HttpContext.Current.Request.InputStream);
        reader.BaseStream.Position = 0;
        criteria = ser.Deserialize<Criteria>(reader.ReadToEnd());

        return Json(_service.ProcessCriteria(id, criteria));
    }
于 2013-03-28T01:40:53.070 に答える
1

フレームワークレベルで機能する答えがあります。私のプロジェクトでは、デフォルト値がサポートするよりも少し大きいデータを扱っていました。したがって、独自の ValueProviderFactory を作成しました。配列にアイテムが含まれていない場合、プロバイダーはそのエントリを完全にスキップしました。代わりに、配列にアイテムがないことを伝える必要があります。必要なコードは次のとおりです。

まず、global.asax Application_Start:

public void Application_Start()
{
    ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<System.Web.Mvc.JsonValueProviderFactory>().FirstOrDefault());
    ValueProviderFactories.Factories.Add(new LargeValueProviderFactory());

次に、必要な他のクラスを次に示します。

using System;
using System.Collections.Generic;
using System.Collections;
using System.Web.Mvc;
using System.IO;
using System.Web.Script.Serialization;
using System.Globalization;

public sealed class LargeValueProviderFactory : System.Web.Mvc.ValueProviderFactory
{    
    public override System.Web.Mvc.IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }

        object jsonData = GetDeserializedObject(controllerContext);
        if (jsonData == null)
        {
            return null;
        }

        Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        AddToBackingStore(backingStore, String.Empty, jsonData);
        return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
    }

    private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
    {
        IDictionary<string, object> d = value as IDictionary<string, object>;
        if (d != null)
        {
            foreach (KeyValuePair<string, object> entry in d)
            {
                AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
            }
            return;
        }

        IList l = value as IList;
        if (l != null)
        {
            for (int i = 0; i < l.Count; i++)
            {
                AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
            }
            if (l.Count == 0)
                backingStore[prefix] = value;
            return;
        }

        // primitive
        backingStore[prefix] = value;
    }

    private static object GetDeserializedObject(ControllerContext controllerContext)
    {
        
        if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
        {
            // not JSON request
            return null;
        }

        StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
        string bodyText = reader.ReadToEnd();
        if (String.IsNullOrEmpty(bodyText))
        {
            // no JSON data
            return null;
        }

        JavaScriptSerializer serializer = new JavaScriptSerializer();
        serializer.MaxJsonLength = Int32.MaxValue;
        object jsonData = serializer.DeserializeObject(bodyText);
        return jsonData;
    }

    private static string MakeArrayKey(string prefix, int index)
    {
        return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
    }

    private static string MakePropertyKey(string prefix, string propertyName)
    {
        return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
    }
}
于 2013-08-04T14:54:08.197 に答える
1

実際の問題はDefaultModelBinder.csobjectListの 711 行目にあり、ビルドに何も含まれていない場合は null が返されると思います。これをチェックしてください:https://lostechies.com/jimmybogard/2013/11/07/null-collectionsarrays-from-mvc-model-binding/

于 2017-01-30T02:40:36.147 に答える
0

コメントとして投稿したものは次のとおりです。

public class Criteria
{
    public decimal? ANullableNumber { get; set; }
    private IList<ObjectInList> _objectsList = new List<ObjectInList>();
    public IList<ObjectInList> ObjectsList 
    { 
        get { return _objectsList; } 
        set { 
            if(value != null) 
                _objectsList = value;
        }
     }
}
于 2013-01-23T12:51:55.900 に答える
-2

これは、「Criteria」クラスで null 許容プロパティ値を定義しないためです。定義しない場合は null になります。

例えば:

  public class Criteria {
    public decimal? ANullableNumber { get; set; }
    public IList<ObjectInList> ObjectsList { get; set; }
  }
  public class Criteria1 {
    private IList<ObjectInList> _ls;
    private decimal? _num;
    public decimal? ANullableNumber {
      get {
        if (_num == null) return 0;
        return _num;
      }
      set {
        _num = value;
      }
    }
    public IList<ObjectInList> ObjectsList {
      get {
        if (_ls == null) _ls = new List<ObjectInList>();
        return _ls;
      }
      set {
        _ls = value;
      }
    }
  }
  public class HomeController : Controller {
    public ActionResult Index() {
      var dd = new Criteria();
      return Json(dd);    //output: {"ANullableNumber":null,"ObjectsList":null}
    }
    public ActionResult Index1() {
      var dd = new Criteria1();
      return Json(dd);    //output: {"ANullableNumber":0,"ObjectsList":[]}
    }
  }
于 2013-11-08T09:46:28.493 に答える