これが私のモデルクラスです:
public class MyModel
{
public Employees[] MyEmpls{get;set;}
public int Id{get;set;}
public OrgName{get;set;}
}
以下のJSON構造オブジェクトをMyEmpls as empty array
とともにMVCコントローラーに渡します。
["Id":12, "MyEmpls":[], "OrgName":"Kekran Mcran"]
コントローラ
[HttpPost]
public ActionResult SaveOrg(MyModel model)
{
//model.MyEmpls is null here
}
mode.MyEmpls
はnullではなく空のc#配列であると想定しています。空の配列を実現するためにカスタムモデルバインダーは必要ですか?
他の回答のいくつかは質問の意味を逃したと思います:なぜデフォルトのMVCモデルバインダーは空のJson配列を空のC#配列ではなくnullにバインドするのですか?
ええと、なぜ彼らがそれをしたのかは言えませんが、それがどこで起こるのかを示すことができます。 MVCのソースは、CodePlexの http://aspnetwebstack.codeplex.com/SourceControl/latest にあります。探しているファイルは ValueProviderResult.cs であり、次の場所にあります。
private static object UnwrapPossibleArrayType(CultureInfo culture, object value, Type destinationType)
{
if (value == null || destinationType.IsInstanceOfType(value))
{
return value;
}
// array conversion results in four cases, as below
Array valueAsArray = value as Array;
if (destinationType.IsArray)
{
Type destinationElementType = destinationType.GetElementType();
if (valueAsArray != null)
{
// case 1: both destination + source type are arrays, so convert each element
IList converted = Array.CreateInstance(destinationElementType, valueAsArray.Length);
for (int i = 0; i < valueAsArray.Length; i++)
{
converted[i] = ConvertSimpleType(culture, valueAsArray.GetValue(i), destinationElementType);
}
return converted;
}
else
{
// case 2: destination type is array but source is single element, so wrap element in array + convert
object element = ConvertSimpleType(culture, value, destinationElementType);
IList converted = Array.CreateInstance(destinationElementType, 1);
converted[0] = element;
return converted;
}
}
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;
}
}
// case 4: both destination + source type are single elements, so convert
return ConvertSimpleType(culture, value, destinationType);
}
}
興味深い部分は「ケース3」です。
else
{
// case 3(a): source is empty array, so can't perform conversion
return null;
}
コンストラクターでモデルの配列を初期化することで、この問題を回避できます。ソースを簡単に読んでも、なぜ空の配列を返せないのか、なぜ返さないのかはわかりませんが、興味深い読み物になるはずです。
これはC#の参照型のデフォルト値であるため、null値を取得しています。空の配列を取得するには、コンストラクターを使用してモデルの配列を初期化する必要があります。ただし、初期化時に配列のサイズを定義する必要があるため、List
などの別のタイプのコレクションを使用した方がよい場合があります。
public class MyModel
{
public List<Employees> MyEmpls{get;set;}
public int Id{get;set;}
public OrgName{get;set;}
public MyModel()
{
MyEmpls = new List<Employees>();
}
}
次に、jsonから空の配列が渡されると、空のリストが取得されます。
本当に配列を使用する必要がある場合は、サイズで初期化するだけです:
public class MyModel
{
public Employees[] MyEmpls{get;set;}
public int Id{get;set;}
public OrgName{get;set;}
public MyModel()
{
MyEmpls = new Employees[/*enter size of array in here*/];
}
}
Model.MyEmplsをnullとして取得している場合は、サーバー側で条件を作成して、次のような例外の発生を停止できます。
if(model.MyEmpls !=null){
...
}
MyEmplsはカスタムクラス配列であり、[]だけを送信しているため、nullになります。
これがお役に立てば幸いです。
値がnullかどうかをチェックするセッターを定義できます
public class MyModel
{
private _myEmpls{get;set;}
public Employees[] MyEmpls{
get{return _myEmpls;}
set{_myEmpls=(value==null?new List<Employees>():value);}
}
public int Id{get;set;}
public OrgName{get;set;}
}
以下のようにモデルバインダーを作成してみてください
public class MyModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
try
{
var request = controllerContext.HttpContext.Request;
return new MyModel
{
MyEmpls = request[] ?? new Employees[0],
Id = request["Id"] ?? "",
OrgName = request["OrgName"] ?? ""
};
}
catch
{
//do required exception handling
}
}
}
Application_Startでモデルバインダーを登録します。
ModelBinders.Binders.Add(typeof(MyModel), new MyModelBinder())
そしてコントローラを次のように変更します
[HttpPost]
public ActionResult SaveOrg([ModelBinder(typeof(MyModelBinder))] MyModel model)
{
//model.MyEmpls is null here
}
[HttpPost]
public ActionResult SaveOrg(MyModel model)
{
var serializer = new JavaScriptSerializer();
var stream = System.Web.HttpContext.Current.Request.InputStream;
var reader = new StreamReader(stream);
stream.Position = 0;
var json = reader.ReadToEnd();
model= serializer.Deserialize<MyModel>(json);
//model.MyEmpls is [] here
}
JavaScriptSerializerは空の配列を適切にデシリアライズします。そのため、渡されたモデルを無視して、入力要求ストリームから再構築できます。おそらく正しい方法ではないかもしれませんが、一度だけ実行する必要がある場合は、いくらかの労力を節約できます。 System.Web.Extensionsを参照する必要があります。