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