C# Json空数组在MVC中反序列化为null

C# Json空数组在MVC中反序列化为null,c#,json,asp.net-mvc-3,json.net,C#,Json,Asp.net Mvc 3,Json.net,我有一个控制器动作,它接收一个整数和一个对象,包含各种属性,其中一个是对象的通用列表。当我将JSON发布到带有填充列表的操作时,所有内容都正确映射,我得到一个包含我发布的对象的列表。但是,如果数组为空,MVC操作会将该属性绑定到空列表的null intead。我希望空数组映射到空数组,而不是null,因为在本例中,空数组意味着集合中没有任何内容,null意味着应该检查数据库以查看集合中以前是否保存了任何内容,但我无法确定需要做哪些更改才能使其正确映射。我们使用Json.Net对返回的对象进行对象

我有一个控制器动作,它接收一个整数和一个对象,包含各种属性,其中一个是对象的通用列表。当我将JSON发布到带有填充列表的操作时,所有内容都正确映射,我得到一个包含我发布的对象的列表。但是,如果数组为空,MVC操作会将该属性绑定到空列表的null intead。我希望空数组映射到空数组,而不是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; }
}
正是在控制器操作中,我在criteria对象中得到的是null而不是空列表。无论是否为其他属性发送空值,都会发生这种情况。不确定对象是否是IList而不是IEnumerable?(包装服务调用的Json方法是我们使用Json.Net来序列化响应返回Json结果的包装器-null在接收到的criteria对象中,而不是在返回中。)


我猜我遗漏了一些非常简单的东西,但我不知道是什么,非常感谢任何帮助。

以下是我作为评论发布的内容:

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;
        }
     }
}
公共类标准
{
公共十进制数?ANullableNumber{get;set;}
私有IList_objectsList=新列表();
公共IList对象列表
{ 
获取{return\u objectsList;}
集合{
if(值!=null)
_objectsList=值;
}
}
}

好的,我花了将近5个小时试图找到解决方案 然后我发现自己正在查看MVC源代码。 我发现这是System.Web.Mvc.ValueProviderResult中的Mvc源代码的一个问题 在第173行:

        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));
    }
public ActionResult ProcessCriteria(int-id,Criteria)
{
var ser=new System.Web.Script.Serialization.JavaScriptSerializer();
StreamReader=新的StreamReader(System.Web.HttpContext.Current.Request.InputStream);
reader.BaseStream.Position=0;
criteria=ser.Deserialize(reader.ReadToEnd());
返回Json(_service.ProcessCriteria(id,criteria));
}

我有一个答案可以在框架级别上使用。在我的项目中,我使用的数据比默认值支持的数据大一点。因此,我创建了自己的ValueProviderFactory。事实证明,如果数组中没有项,则提供程序会完全跳过该项。相反,我们只需要告诉它数组中没有项目。这是您需要的代码

首先,global.asax应用程序启动:

public void Application_Start()
{
    ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<System.Web.Mvc.JsonValueProviderFactory>().FirstOrDefault());
    ValueProviderFactories.Factories.Add(new LargeValueProviderFactory());
公共作废应用程序\u Start()
{
valueProviderFactorys.Factories.Remove(valueProviderFactorys.Factories.OfType().FirstOrDefault());
valueProviderFactorys.Factories.Add(新的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;
    }
}
使用系统;
使用System.Collections.Generic;
使用系统集合;
使用System.Web.Mvc;
使用System.IO;
使用System.Web.Script.Serialization;
利用制度全球化;
公共密封类LargeValueProviderFactory:System.Web.Mvc.ValueProviderFactory
{    
public override System.Web.Mvc.IValueProvider GetValueProvider(ControllerContext ControllerContext)
{
如果(controllerContext==null)
{
抛出新ArgumentNullException(“controllerContext”);
}
object jsonData=GetDeserializedObject(controllerContext);
if(jsonData==null)
{
返回null;
}
Dictionary backingStore=新字典(StringComparer.OrdinalingOreCase);
AddToBackStore(backingStore,String.Empty,jsonData);
返回新的DictionaryValueProvider(backingStore,CultureInfo.CurrentCulture);
}
私有静态void addToBackStore(字典备份存储、字符串前缀、对象值)
{
IDictionary d=作为IDictionary的值;
如果(d!=null)
{
foreach(d中的KeyValuePair条目)
{
AddToBackStore(backingStore,MakePropertyKey(前缀,entry.Key),entry.Value);
}
返回;
}
IList l=作为IList的值;
如果(l!=null)
{
对于(int i=0;iusing 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;
    }
}
  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":[]}
    }
  }
public class Criteria
{
    public decimal? ANullableNumber { get; set; }
    public IList<ObjectInList> ObjectsList { get; set; } = new List<ObjectInList>();
}