C# ModelBinder反序列化任意json

C# ModelBinder反序列化任意json,c#,asp.net-mvc,C#,Asp.net Mvc,对于以下数据结构,我无法使默认模型绑定器按预期工作: json: IPPresentationData模型: IContext模型: 而不是: { 'table': [ [...] ] } 我想我需要覆盖System.Web.Mvc.DefaultModelBinderBindModel,只覆盖SlideData.Context属性,但我不确定从哪里开始 这里的任何帮助都将不胜感激。事实证明,我们需要的是一个支持ExpandoObject的IModelBinder 代码如下:

对于以下数据结构,我无法使默认模型绑定器按预期工作:

json:

IPPresentationData模型:

IContext模型:

而不是:

{
  '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;
    }
}