我无法让默认模型绑定器按预期的方式运行以下数据结构:
JSON:
{
template: "path/to/template",
slides: [{
index: 0,
context: {
foo: "bar"
}
}, {
index: 1,
context: {
foo: 'bar!',
table: [
['Price', 'Revenue', 'Profit'],
[$5', 100, 20],
['$10', 320, 4],
['$7', 50, 2]
]
}
}]
}
IPresentationData
型号:
public interface IPresentationData
{
public string Template { get; set; }
public ICollection<SlideData> Slides { get; set; }
}
ISlideData
型号:
public interface ISlideData
{
public int Index { get; set; }
public IContext Context { get; set; }
}
IContext
型号:
public interface IContext : IDictionary<string, dynamic>
{
}
默认模型绑定器工作正常,但SlideData.Context
除外,它可以是任意大/深的对象。
例如,json.context.table
被反序列化为:
{
'table[0][0]': 'Price',
'table[0][1]': 'Revenue',
...
}
而不是:
{
'table': [
[...]
]
}
我认为我需要仅为System.Web.Mvc.DefaultModelBinder#BindModel
属性覆盖SlideData.Context
,但我不确定从哪里开始。
非常感谢任何帮助。
答案 0 :(得分:4)
原来需要的是支持IModelBinder
的{{1}}。
以下是代码:
在ExpandoObject
:
HttpApplication#Application_Start
自定义活页夹:
ModelBinders.Binders.Add(typeof(Context), new DynamicDictionaryModelBinder());
ModelBinders.Binders.Add(typeof(ExpandoObject), new DynamicDictionaryModelBinder());
json已经被内置的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
提供正确的类型以及正确的访问者。例如Binder#GetBinder(Type)
+ ICollection<ExpandoObject>
vs revenue
+ object
。