40

Here is my model class:

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

Passing the below JSON structure object with MyEmpls as empty array to MVC controller.

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

Controller

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

I am expecting mode.MyEmpls to be an empty c# array, not a null. Is a custom model binder necessary to achieve an empty array?

Derek Greer
16.4k5 gold badges50 silver badges58 bronze badges
asked Apr 16, 2014 at 11:34
2
  • And MyEmpls isn't null when you provide a Employee via JSON? Commented Apr 28, 2014 at 12:26
  • Why don't u check in your action method if myEmpls is null then define it as an empty c# array? Commented Apr 28, 2014 at 12:29

7 Answers 7

38

I think that some of the other answers have missed the meaning of the question: why does the default MVC model binder bind an empty Json array to null instead of an empty C# array?

Well, I can't tell you why they did that, but I can show you where it happens. The source for MVC can be found on CodePlex here: http://aspnetwebstack.codeplex.com/SourceControl/latest. The file you're looking for is ValueProviderResult.cs where you can see:

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

The interesting part is "case 3":

else
{
 // case 3(a): source is empty array, so can't perform conversion
 return null;
}

You can sidestep this issue by initialising your array on the model in its constructor. In my quick reading of the source I can't tell you why they can't return an empty array or why they decide not to, but it should make for interesting reading.

answered May 1, 2014 at 9:29

7 Comments

It's seems the issue is that destinationType.IsArray returns false for List<> class, where it would work for Array type.
In the code the OP posted he has used Employee[] rather than List<Employee>, so unless that's a mistake I'm at a loss...
I would think the actual problem is in DefaultModelBinder github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/… line 711 where it returns null if the built objectList contains nothing. Check this out: lostechies.com/jimmybogard/2013/11/07/…
Is there a way to override the definition of DefaultModelBinder, so that an Empty Array is retained as an Empty Array after deserialization ? The idea is that my Controller method is associated with a HTTPPatch and this specific array is expected to be empty, for it to be processed.
Note that in MVC Core this behavior has been changed to the expected behavior of empty JSON arrays being bound to an empty collection.
|
29
+25

You're getting a null value as this is the default value for a reference type in C#. In order to get an empty array you will need to initialise the array in your model using a constructor. However as you will need to define the size of the array when it's initialized it might be better using another type of collection such as a 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>();
 }
}

You will then get an empty list when an empty array is passed from the json.

If you really have to use an array just initialise it with a size:

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*/];
 }
}
answered Apr 28, 2014 at 13:19

2 Comments

It is a WCF proxy class that is being used as MyModel. So i think he might not have control to change Employee[] to List<Employee>
Null and empty mean very different things. In my case - specifying changes to a model. Null means ignore this property, leave it as it is. Empty means remove all elements.
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 deserializes empty arrays properly. So you can ignore the passed-in model and rebuild it from the input request stream. Probably not the correct way, but if you only need to do it once it saves some effort. You'll need to reference System.Web.Extensions.

answered Feb 12, 2016 at 5:15

Comments

-1

If you are getting model.MyEmpls as null then you can create a condition on server side to stop raising exception like:

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

And you are getting it null because your MyEmpls is a Custom Class array and you are sending just [].

Hope this helps you.

answered Apr 28, 2014 at 12:23

Comments

-1

Try to create a model binder as in below

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

In Application_Start register the model binder

ModelBinders.Binders.Add(typeof(MyModel), new MyModelBinder())

And modify the controller as

[HttpPost]
public ActionResult SaveOrg([ModelBinder(typeof(MyModelBinder))] MyModel model)
{
 //model.MyEmpls is null here
}
answered May 2, 2014 at 8:08

Comments

-1

you can define a setter that checks if the value is 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;}
}
answered May 2, 2014 at 10:08

Comments

-2

The default behavior of C# array initialization is to null not empty array

So if you send an empty array you will not initiate an empty array, but you will trigger the default initialization to null

See the following link http://www.dotnetperls.com/null-array

Kushan Randima
2,3005 gold badges38 silver badges64 bronze badges
answered Apr 28, 2014 at 13:09

1 Comment

I think the issue the question was getting at was that [] in JSON was deserialised as null it C# rather than new [] {}. This behaviour seems strange to me since I consider an empty C# array to be closer to a JSON array than null.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.