2013-01-23 10 views
21

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

オブジェクトが渡される: "{\" のID \ ":137、\" 基準\ ":{\" ObjectsList \ ":[]}}"

要求

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

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

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

これは、基準オブジェクトに私がnullの代わりに、空のリストを取得していますコントローラのアクションです。他のプロパティに対してnullを送信するかどうかは、発生します。オブジェクトがIEnumerableではなくIListであるかどうかは不明です。 (サービスコールをラップするJsonメソッドは、Json.Netを使用してjson.Netを使用してjsonの結果を返すラッパーです。戻り値ではなく、受信した基準オブジェクトにnullが含まれています)。

私はそれを推測しています私は行方不明ですが、私は何かを助けることはできません。ここで

+0

いくつかのコードを入力すれば簡単です。 1行のコードが100語よりも優れています:D –

+0

この場合、合意に達しましたが、私はコントローラ/ベースコントローラ全体と関連するjson投稿を投稿するのに手間がかかります。私は何かを意味する何かを投稿しようとしますが、それまではjsonは空の配列を表示しています。ポストされてモデルにバインドされる前に、Listは空のリストではなくnullです。コントローラの階層の一部は他の開発者によって書かれているので、私は確かに言うことはできませんが、jsonのデシリアライゼーションのオーバーライドされた実装を見つけることができません。 –

+0

この質問を確認する:http://stackoverflow.com/q/14203150/29555と2番目の回答 – marto

答えて

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

私は上記のコメントで述べたように、このリストにもnull状態を持たせたいので、この場合nullを送信しようとすると、空のリストとして表示されます。望ましい行動ではない。 –

10

[OK]を、私は解決策 を見つけるしようと、ほぼ5時間、この問題に直面していたし、私は自分自身がMVCのソースコードで探して見つけました。あなたはソースが空の配列の場合、それはnullを返します見ることができるよう

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

: と私は、これはSystem.Web.Mvc.ValueProviderResultでMVCソースコードに問題がライン173で であることがわかりました。

ので、私はそれを回避する方法を見つける必要があり、その後、私は、古き良き時代に我々は、逆シリアル化をしていたか覚えている: これは、あなたが欲しいものを得るだろうかです:

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)); 
    } 
+1

これは明らかなバグです。 問題を投稿したりコミットしたりできます。http://aspnetwebstack.codeplex.com/ –

+1

実際の問題はDefaultModelBinder https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/srcにあると思います/System.Web.Mvc/DefaultModelBinder.cs行711ビルドされた 'objectList'に何も含まれていない場合はnullを返します。これをチェックしてください:https://lostechies.com/jimmybogard/2013/11/07/null-collectionsarrays-from-mvc-model-binding/ – bigbearzhu

0

は私が持っていますあなたのための答えは、フレームワークレベルで動作します。私のプロジェクトでは、デフォルト値よりも少し大きいデータを扱っていました。したがって、私は独自のValueProviderFactoryを作成しました。配列に項目がない場合、プロバイダはその項目を完全にスキップします。代わりに、配列内に項目がないことを伝えるだけです。ここにあなたが必要とするコードがあります。

まず、グローバルです。asaxのApplication_Start:

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

第二には、ここにあなたが必要とする他のクラスです:

#region <<Usings>> 

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

#endregion 

/// <summary> 
/// This class is to ensure we can receive large JSON data from the client because the default is a bit too small. 
/// </summary> 
/// <remarks>This class is from the web.</remarks> 
public sealed class LargeValueProviderFactory : System.Web.Mvc.ValueProviderFactory 
{ 

    #region <<Constructors>> 

    /// <summary> 
    /// Default constructor. 
    /// </summary> 
    public LargeValueProviderFactory() 
     : base() 
    { 
     // Nothing to do 
    } 

    #endregion 

    #region <<GetValueProvider>> 

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

    #endregion 

    #region << Helper Methods >> 

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

    #endregion 

} 
-2

あなたの「基準」クラスの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":[]} 
    } 
    } 
関連する問題