C# ModelBinder反序列化任意json
对于以下数据结构,我无法使默认模型绑定器按预期工作: json: IPPresentationData模型: IContext模型: 而不是:C# ModelBinder反序列化任意json,c#,asp.net-mvc,C#,Asp.net Mvc,对于以下数据结构,我无法使默认模型绑定器按预期工作: json: IPPresentationData模型: IContext模型: 而不是: { 'table': [ [...] ] } 我想我需要覆盖System.Web.Mvc.DefaultModelBinderBindModel,只覆盖SlideData.Context属性,但我不确定从哪里开始 这里的任何帮助都将不胜感激。事实证明,我们需要的是一个支持ExpandoObject的IModelBinder 代码如下:
{
'table': [
[...]
]
}
我想我需要覆盖System.Web.Mvc.DefaultModelBinderBindModel,只覆盖SlideData.Context属性,但我不确定从哪里开始
这里的任何帮助都将不胜感激。事实证明,我们需要的是一个支持ExpandoObject的IModelBinder 代码如下: 在HttpApplicationApplication\u启动中: 自定义活页夹:
public class DynamicDictionaryModelBinder : DefaultModelBinder
{
public override object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var model = bindingContext.Model;
var modelType = bindingContext.ModelType;
if (model == null)
{
model = this.CreateModel(controllerContext, bindingContext, modelType);
}
var dictionaryBindingContext = new ModelBindingContext()
{
ModelMetadata =
ModelMetadataProviders.Current
.GetMetadataForType(() => model, modelType),
ModelName = bindingContext.ModelName,
ModelState = bindingContext.ModelState,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
return this.UpdateDynamicDictionary(controllerContext, dictionaryBindingContext);
}
private static KeyValuePair<string, object> CreateEntryForModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
Type valueType,
IModelBinder valueBinder,
string modelName,
string modelKey)
{
var valueBindingContext = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, valueType),
ModelName = modelName,
ModelState = bindingContext.ModelState,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
var thisValue = valueBinder.BindModel(controllerContext, valueBindingContext);
return new KeyValuePair<string, object>(modelKey, thisValue);
}
private object UpdateDynamicDictionary(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var modelList = new List<KeyValuePair<string, object>>();
var enumerableValueProvider = bindingContext.ValueProvider as IEnumerableValueProvider;
if (enumerableValueProvider != null)
{
var keys = enumerableValueProvider.GetKeysFromPrefix(bindingContext.ModelName);
var groups = keys.GroupBy((k) => k.Key.Split('[')[0]);
foreach (var group in groups)
{
if (group.Count() > 1)
{
var valueType = typeof(ICollection<ExpandoObject>);
modelList.Add(
CreateEntryForModel(
controllerContext,
bindingContext,
valueType,
Binders.GetBinder(valueType),
bindingContext.ModelName + '.' + group.Key,
group.Key));
}
else
{
var item = group.Single();
var value = bindingContext.ValueProvider.GetValue(item.Value);
var valueType = value != null && value.RawValue != null ?
typeof(object) : typeof(ExpandoObject);
modelList.Add(
CreateEntryForModel(
controllerContext,
bindingContext,
valueType,
Binders.GetBinder(valueType),
item.Value,
item.Key));
}
}
}
var dictionary = (IDictionary<string, object>)bindingContext.Model;
foreach (var kvp in modelList)
{
dictionary[kvp.Key] = kvp.Value;
}
return dictionary;
}
}
内置的IValueProvider堆栈已经对json进行了正确的序列化,只需为BinderGetBinderType提供正确的类型以及正确的访问器。例如ICollection+revenue vs object+revenue[0][0]。您看到了吗?这很好,但是当您可以选择与Json.Net一起使用dynamic时,context属性是完全动态的。就这么找吧。
public interface ISlideData
{
public int Index { get; set; }
public IContext Context { get; set; }
}
public interface IContext : IDictionary<string, dynamic>
{
}
{
'table[0][0]': 'Price',
'table[0][1]': 'Revenue',
...
}
{
'table': [
[...]
]
}
ModelBinders.Binders.Add(typeof(Context), new DynamicDictionaryModelBinder());
ModelBinders.Binders.Add(typeof(ExpandoObject), new DynamicDictionaryModelBinder());
public class DynamicDictionaryModelBinder : DefaultModelBinder
{
public override object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var model = bindingContext.Model;
var modelType = bindingContext.ModelType;
if (model == null)
{
model = this.CreateModel(controllerContext, bindingContext, modelType);
}
var dictionaryBindingContext = new ModelBindingContext()
{
ModelMetadata =
ModelMetadataProviders.Current
.GetMetadataForType(() => model, modelType),
ModelName = bindingContext.ModelName,
ModelState = bindingContext.ModelState,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
return this.UpdateDynamicDictionary(controllerContext, dictionaryBindingContext);
}
private static KeyValuePair<string, object> CreateEntryForModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
Type valueType,
IModelBinder valueBinder,
string modelName,
string modelKey)
{
var valueBindingContext = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, valueType),
ModelName = modelName,
ModelState = bindingContext.ModelState,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
var thisValue = valueBinder.BindModel(controllerContext, valueBindingContext);
return new KeyValuePair<string, object>(modelKey, thisValue);
}
private object UpdateDynamicDictionary(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var modelList = new List<KeyValuePair<string, object>>();
var enumerableValueProvider = bindingContext.ValueProvider as IEnumerableValueProvider;
if (enumerableValueProvider != null)
{
var keys = enumerableValueProvider.GetKeysFromPrefix(bindingContext.ModelName);
var groups = keys.GroupBy((k) => k.Key.Split('[')[0]);
foreach (var group in groups)
{
if (group.Count() > 1)
{
var valueType = typeof(ICollection<ExpandoObject>);
modelList.Add(
CreateEntryForModel(
controllerContext,
bindingContext,
valueType,
Binders.GetBinder(valueType),
bindingContext.ModelName + '.' + group.Key,
group.Key));
}
else
{
var item = group.Single();
var value = bindingContext.ValueProvider.GetValue(item.Value);
var valueType = value != null && value.RawValue != null ?
typeof(object) : typeof(ExpandoObject);
modelList.Add(
CreateEntryForModel(
controllerContext,
bindingContext,
valueType,
Binders.GetBinder(valueType),
item.Value,
item.Key));
}
}
}
var dictionary = (IDictionary<string, object>)bindingContext.Model;
foreach (var kvp in modelList)
{
dictionary[kvp.Key] = kvp.Value;
}
return dictionary;
}
}