为什么 ASP.Net MVC 模型绑定器将空 JSON 数组绑定到 null?

问题描述 投票:0回答:8

这是我的模型类:

public class MyModel
{
    public Employees[] MyEmpls{get;set;}
    public int Id{get;set;}
    public OrgName{get;set;}
}

将以下带有

MyEmpls as empty array
的 JSON 结构对象传递给 MVC 控制器。

["Id":12, "MyEmpls":[], "OrgName":"Kekran Mcran"]

控制器

[HttpPost]
public ActionResult SaveOrg(MyModel model)
{
  //model.MyEmpls is null here
}

我期望

mode.MyEmpls
是一个空的 C# 数组,而不是 null。是否需要自定义模型绑定器才能实现空数组?

c# asp.net-mvc json wcf model-binding
8个回答
38
投票

我认为其他一些答案忽略了问题的含义:为什么默认的 MVC 模型绑定器将空 Json 数组绑定到 null 而不是空 C# 数组?

好吧,我无法告诉你他们为什么这样做,但我可以告诉你它发生在哪里。 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;
}

您可以通过在模型的构造函数中初始化模型上的数组来回避此问题。在我对源代码的快速阅读中,我无法告诉您为什么他们不能返回空数组或为什么他们决定不返回,但它应该会引起有趣的阅读。


29
投票

您将得到一个空值,因为这是 C# 中引用类型的默认值。为了获得空数组,您需要使用构造函数初始化模型中的数组。但是,由于您需要在初始化时定义数组的大小,因此最好使用其他类型的集合,例如

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*/];
    }
}

1
投票
[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。


0
投票

如果将 array = [] 从 ajax 发送到 c# 控制器 它在 C# 方法中显示 array =null not count =0 解决这个问题 []='[]'


-1
投票

如果您将 model.MyEmpls 设置为 null,那么您可以在服务器端创建一个条件来停止引发异常,例如:

if(model.MyEmpls !=null){
...
}

您得到它为空,因为您的 MyEmpls 是一个自定义类数组,并且您只发送 []。

希望这对您有帮助。


-1
投票

尝试创建一个模型绑定器,如下所示

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
}

-1
投票

您可以定义一个setter来检查值是否为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;}
}

-2
投票

C# 数组初始化的默认行为是 null 而不是空数组

因此,如果您发送一个空数组,您将不会启动空数组,但会触发默认初始化为 null

请参阅以下链接 http://www.dotnetperls.com/null-array

© www.soinside.com 2019 - 2024. All rights reserved.